diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-08-28 12:36:58 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-08-28 12:36:58 -0300 |
commit | 5382a22e0eea878339c504b2a9a3b36bcd839fcc (patch) | |
tree | f4208ffe221d2f42920c751c3624f17d89b07079 | |
parent | 8c8a91f2ef7acccb99e3737913faad8d48b39571 (diff) | |
download | lua-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.h | 14 | ||||
-rw-r--r-- | lobject.c | 6 | ||||
-rw-r--r-- | lvm.c | 18 | ||||
-rw-r--r-- | lvm.h | 1 | ||||
-rw-r--r-- | testes/math.lua | 66 |
5 files changed, 87 insertions, 18 deletions
@@ -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 */ |
@@ -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 | } |
@@ -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 | */ | ||
661 | lua_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)); |
@@ -116,6 +116,7 @@ LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); | |||
116 | LUAI_FUNC void luaV_concat (lua_State *L, int total); | 116 | LUAI_FUNC void luaV_concat (lua_State *L, int total); |
117 | LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); | 117 | LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); |
118 | LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); | 118 | LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); |
119 | LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y); | ||
119 | LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); | 120 | LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); |
120 | LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); | 121 | LUAI_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 | ||
4 | print("testing numbers and math lib") | 4 | print("testing numbers and math lib") |
@@ -541,9 +541,73 @@ assert(eqT(-4 % 3, 2)) | |||
541 | assert(eqT(4 % -3, -2)) | 541 | assert(eqT(4 % -3, -2)) |
542 | assert(eqT(-4.0 % 3, 2.0)) | 542 | assert(eqT(-4.0 % 3, 2.0)) |
543 | assert(eqT(4 % -3.0, -2.0)) | 543 | assert(eqT(4 % -3.0, -2.0)) |
544 | assert(eqT(4 % -5, -1)) | ||
545 | assert(eqT(4 % -5.0, -1.0)) | ||
546 | assert(eqT(4 % 5, 4)) | ||
547 | assert(eqT(4 % 5.0, 4.0)) | ||
548 | assert(eqT(-4 % -5, -4)) | ||
549 | assert(eqT(-4 % -5.0, -4.0)) | ||
550 | assert(eqT(-4 % 5, 1)) | ||
551 | assert(eqT(-4 % 5.0, 1.0)) | ||
552 | assert(eqT(4.25 % 4, 0.25)) | ||
553 | assert(eqT(10.0 % 2, 0.0)) | ||
554 | assert(eqT(-10.0 % 2, 0.0)) | ||
555 | assert(eqT(-10.0 % -2, 0.0)) | ||
544 | assert(math.pi - math.pi % 1 == 3) | 556 | assert(math.pi - math.pi % 1 == 3) |
545 | assert(math.pi - math.pi % 0.001 == 3.141) | 557 | assert(math.pi - math.pi % 0.001 == 3.141) |
546 | 558 | ||
559 | do -- 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)) | ||
577 | end | ||
578 | |||
579 | |||
580 | -- basic consistency between integer modulo and float modulo | ||
581 | for 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 | ||
587 | end | ||
588 | |||
589 | for 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 | ||
595 | end | ||
596 | |||
597 | do -- 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 | ||
609 | end | ||
610 | |||
547 | assert(eqT(minint % minint, 0)) | 611 | assert(eqT(minint % minint, 0)) |
548 | assert(eqT(maxint % maxint, 0)) | 612 | assert(eqT(maxint % maxint, 0)) |
549 | assert((minint + 1) % minint == minint + 1) | 613 | assert((minint + 1) % minint == minint + 1) |