aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-08-28 12:36:58 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-08-28 12:36:58 -0300
commit5382a22e0eea878339c504b2a9a3b36bcd839fcc (patch)
treef4208ffe221d2f42920c751c3624f17d89b07079
parent8c8a91f2ef7acccb99e3737913faad8d48b39571 (diff)
downloadlua-5382a22e0eea878339c504b2a9a3b36bcd839fcc.tar.gz
lua-5382a22e0eea878339c504b2a9a3b36bcd839fcc.tar.bz2
lua-5382a22e0eea878339c504b2a9a3b36bcd839fcc.zip
Corrections in the implementation of '%' for floats.
The multiplication (m*b) used to test whether 'm' is non-zero and 'm' and 'b' have different signs can underflow for very small numbers, giving a wrong result. The use of explicit comparisons solves this problem. This commit also adds several new tests for '%' (both for floats and for integers) to exercise more corner cases, such as very large and very small values.
-rw-r--r--llimits.h14
-rw-r--r--lobject.c6
-rw-r--r--lvm.c18
-rw-r--r--lvm.h1
-rw-r--r--testes/math.lua66
5 files changed, 87 insertions, 18 deletions
diff --git a/llimits.h b/llimits.h
index 6afa8997..e91310a0 100644
--- a/llimits.h
+++ b/llimits.h
@@ -293,15 +293,17 @@ typedef unsigned long Instruction;
293#endif 293#endif
294 294
295/* 295/*
296** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when 296** modulo: defined as 'a - floor(a/b)*b'; the direct computation
297** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of 297** using this definition has several problems with rounding errors,
298** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b) 298** so it is better to use 'fmod'. 'fmod' gives the result of
299** ~= floor(a/b)'. That happens when the division has a non-integer 299** 'a - trunc(a/b)*b', and therefore must be corrected when
300** negative result, which is equivalent to the test below. 300** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a
301** non-integer negative result, which is equivalent to the tests below.
301*/ 302*/
302#if !defined(luai_nummod) 303#if !defined(luai_nummod)
303#define luai_nummod(L,a,b,m) \ 304#define luai_nummod(L,a,b,m) \
304 { (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); } 305 { (void)L; (m) = l_mathop(fmod)(a,b); \
306 if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); }
305#endif 307#endif
306 308
307/* exponentiation */ 309/* exponentiation */
diff --git a/lobject.c b/lobject.c
index 79cf55b8..d011c85f 100644
--- a/lobject.c
+++ b/lobject.c
@@ -106,11 +106,7 @@ static lua_Number numarith (lua_State *L, int op, lua_Number v1,
106 case LUA_OPPOW: return luai_numpow(L, v1, v2); 106 case LUA_OPPOW: return luai_numpow(L, v1, v2);
107 case LUA_OPIDIV: return luai_numidiv(L, v1, v2); 107 case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
108 case LUA_OPUNM: return luai_numunm(L, v1); 108 case LUA_OPUNM: return luai_numunm(L, v1);
109 case LUA_OPMOD: { 109 case LUA_OPMOD: return luaV_modf(L, v1, v2);
110 lua_Number m;
111 luai_nummod(L, v1, v2, m);
112 return m;
113 }
114 default: lua_assert(0); return 0; 110 default: lua_assert(0); return 0;
115 } 111 }
116} 112}
diff --git a/lvm.c b/lvm.c
index add26942..dd6a660b 100644
--- a/lvm.c
+++ b/lvm.c
@@ -655,6 +655,16 @@ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
655} 655}
656 656
657 657
658/*
659** Float modulus
660*/
661lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
662 lua_Number r;
663 luai_nummod(L, m, n, r);
664 return r;
665}
666
667
658/* number of bits in an integer */ 668/* number of bits in an integer */
659#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) 669#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
660 670
@@ -1142,10 +1152,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1142 setivalue(s2v(ra), luaV_mod(L, ivalue(rb), ic)); 1152 setivalue(s2v(ra), luaV_mod(L, ivalue(rb), ic));
1143 } 1153 }
1144 else if (tonumberns(rb, nb)) { 1154 else if (tonumberns(rb, nb)) {
1145 lua_Number m;
1146 lua_Number nc = cast_num(ic); 1155 lua_Number nc = cast_num(ic);
1147 luai_nummod(L, nb, nc, m); 1156 setfltvalue(s2v(ra), luaV_modf(L, nb, nc));
1148 setfltvalue(s2v(ra), m);
1149 } 1157 }
1150 else 1158 else
1151 Protect(luaT_trybiniTM(L, rb, ic, 0, ra, TM_MOD)); 1159 Protect(luaT_trybiniTM(L, rb, ic, 0, ra, TM_MOD));
@@ -1370,9 +1378,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1370 setivalue(s2v(ra), luaV_mod(L, ib, ic)); 1378 setivalue(s2v(ra), luaV_mod(L, ib, ic));
1371 } 1379 }
1372 else if (tonumberns(rb, nb) && tonumberns(rc, nc)) { 1380 else if (tonumberns(rb, nb) && tonumberns(rc, nc)) {
1373 lua_Number m; 1381 setfltvalue(s2v(ra), luaV_modf(L, nb, nc));
1374 luai_nummod(L, nb, nc, m);
1375 setfltvalue(s2v(ra), m);
1376 } 1382 }
1377 else 1383 else
1378 Protect(luaT_trybinTM(L, rb, rc, ra, TM_MOD)); 1384 Protect(luaT_trybinTM(L, rb, rc, ra, TM_MOD));
diff --git a/lvm.h b/lvm.h
index d58daacd..8ead0c50 100644
--- a/lvm.h
+++ b/lvm.h
@@ -116,6 +116,7 @@ LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci);
116LUAI_FUNC void luaV_concat (lua_State *L, int total); 116LUAI_FUNC void luaV_concat (lua_State *L, int total);
117LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); 117LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y);
118LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); 118LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y);
119LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y);
119LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); 120LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y);
120LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); 121LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);
121 122
diff --git a/testes/math.lua b/testes/math.lua
index 853dc20f..b387977e 100644
--- a/testes/math.lua
+++ b/testes/math.lua
@@ -1,4 +1,4 @@
1-- $Id: testes/math.lua $ 1-- $Id: testes/math.lua 2018-07-25 15:31:04 -0300 $
2-- See Copyright Notice in file all.lua 2-- See Copyright Notice in file all.lua
3 3
4print("testing numbers and math lib") 4print("testing numbers and math lib")
@@ -541,9 +541,73 @@ assert(eqT(-4 % 3, 2))
541assert(eqT(4 % -3, -2)) 541assert(eqT(4 % -3, -2))
542assert(eqT(-4.0 % 3, 2.0)) 542assert(eqT(-4.0 % 3, 2.0))
543assert(eqT(4 % -3.0, -2.0)) 543assert(eqT(4 % -3.0, -2.0))
544assert(eqT(4 % -5, -1))
545assert(eqT(4 % -5.0, -1.0))
546assert(eqT(4 % 5, 4))
547assert(eqT(4 % 5.0, 4.0))
548assert(eqT(-4 % -5, -4))
549assert(eqT(-4 % -5.0, -4.0))
550assert(eqT(-4 % 5, 1))
551assert(eqT(-4 % 5.0, 1.0))
552assert(eqT(4.25 % 4, 0.25))
553assert(eqT(10.0 % 2, 0.0))
554assert(eqT(-10.0 % 2, 0.0))
555assert(eqT(-10.0 % -2, 0.0))
544assert(math.pi - math.pi % 1 == 3) 556assert(math.pi - math.pi % 1 == 3)
545assert(math.pi - math.pi % 0.001 == 3.141) 557assert(math.pi - math.pi % 0.001 == 3.141)
546 558
559do -- very small numbers
560 local i, j = 0, 20000
561 while i < j do
562 local m = (i + j) // 2
563 if 10^-m > 0 then
564 i = m + 1
565 else
566 j = m
567 end
568 end
569 -- 'i' is the smallest possible ten-exponent
570 local b = 10^-(i - (i // 10)) -- a very small number
571 assert(b > 0 and b * b == 0)
572 local delta = b / 1000
573 assert(eq((2.1 * b) % (2 * b), (0.1 * b), delta))
574 assert(eq((-2.1 * b) % (2 * b), (2 * b) - (0.1 * b), delta))
575 assert(eq((2.1 * b) % (-2 * b), (0.1 * b) - (2 * b), delta))
576 assert(eq((-2.1 * b) % (-2 * b), (-0.1 * b), delta))
577end
578
579
580-- basic consistency between integer modulo and float modulo
581for i = -10, 10 do
582 for j = -10, 10 do
583 if j ~= 0 then
584 assert((i + 0.0) % j == i % j)
585 end
586 end
587end
588
589for i = 0, 10 do
590 for j = -10, 10 do
591 if j ~= 0 then
592 assert((2^i) % j == (1 << i) % j)
593 end
594 end
595end
596
597do -- precision of module for large numbers
598 local i = 10
599 while (1 << i) > 0 do
600 assert((1 << i) % 3 == i % 2 + 1)
601 i = i + 1
602 end
603
604 i = 10
605 while 2^i < math.huge do
606 assert(2^i % 3 == i % 2 + 1)
607 i = i + 1
608 end
609end
610
547assert(eqT(minint % minint, 0)) 611assert(eqT(minint % minint, 0))
548assert(eqT(maxint % maxint, 0)) 612assert(eqT(maxint % maxint, 0))
549assert((minint + 1) % minint == minint + 1) 613assert((minint + 1) % minint == minint + 1)