From 049cf14cf9f82a07387df4d0c9bdba5ba2fef22f Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 21 Nov 2014 10:15:00 -0200 Subject: 'x//y' extended to floats + more comments about module and floor division operations --- luaconf.h | 18 ++++++++++++++++-- lvm.c | 49 +++++++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/luaconf.h b/luaconf.h index 78dde74a..5b61e4a2 100644 --- a/luaconf.h +++ b/luaconf.h @@ -1,5 +1,5 @@ /* -** $Id: luaconf.h,v 1.227 2014/11/02 19:35:39 roberto Exp roberto $ +** $Id: luaconf.h,v 1.228 2014/11/19 15:00:42 roberto Exp roberto $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ @@ -468,9 +468,23 @@ /* the following operations need the math library */ #if defined(lobject_c) || defined(lvm_c) #include + +/* floor division (defined as 'floor(a/b)') */ +#define luai_numidiv(L,a,b) ((void)L, l_mathop(floor)((a)/(b))) + +/* +** module: defined as 'a - floor(a/b)*b'; the previous definition gives +** NaN when 'b' is huge, but the result should be 'a'. 'fmod' gives the +** result of 'a - trunc(a/b)*b', and therefore must be corrected when +** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a +** non-integer negative result, which is equivalent to the test below +*/ #define luai_nummod(L,a,b,m) \ - { (m) = l_mathop(fmod)(a,b); if ((m) != 0 && (a)*(b) < 0) (m) += (b); } + { (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); } + +/* exponentiation */ #define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) + #endif /* these are quite standard operations */ diff --git a/lvm.c b/lvm.c index 28a3aab8..5e7764bd 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.228 2014/11/03 20:07:47 roberto Exp roberto $ +** $Id: lvm.c,v 2.229 2014/11/19 15:05:15 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -428,8 +428,10 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { /* -** Integer division; return 'm // n'. (Assume that C division with -** negative operands follows C99 behavior.) +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. */ lua_Integer luaV_div (lua_State *L, lua_Integer m, lua_Integer n) { if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ @@ -438,18 +440,18 @@ lua_Integer luaV_div (lua_State *L, lua_Integer m, lua_Integer n) { return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ } else { - lua_Integer d = m / n; /* perform division */ - if ((m ^ n) >= 0 || m % n == 0) /* same signal or no rest? */ - return d; - else - return d - 1; /* correct 'div' for negative case */ + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; } } /* ** Integer modulus; return 'm % n'. (Assume that C '%' with -** negative operands follows C99 behavior.) +** negative operands follows C99 behavior. See previous comment +** about luaV_div.) */ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ @@ -459,10 +461,9 @@ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { } else { lua_Integer r = m % n; - if (r == 0 || (m ^ n) >= 0) /* no rest or same signal? */ - return r; - else - return r + n; /* correct 'mod' for negative case */ + if (r != 0 && (m ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; } } @@ -778,15 +779,6 @@ void luaV_execute (lua_State *L) { } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_DIV)); } ) - vmcase(OP_IDIV, /* integer division */ - TValue *rb = RKB(i); - TValue *rc = RKC(i); - lua_Integer ib; lua_Integer ic; - if (tointeger(rb, &ib) && tointeger(rc, &ic)) { - setivalue(ra, luaV_div(L, ib, ic)); - } - else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_IDIV)); } - ) vmcase(OP_BAND, TValue *rb = RKB(i); TValue *rc = RKC(i); @@ -847,6 +839,19 @@ void luaV_execute (lua_State *L) { } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_MOD)); } ) + vmcase(OP_IDIV, /* floor division */ + TValue *rb = RKB(i); + TValue *rc = RKC(i); + lua_Number nb; lua_Number nc; + if (ttisinteger(rb) && ttisinteger(rc)) { + lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); + setivalue(ra, luaV_div(L, ib, ic)); + } + else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { + setfltvalue(ra, luai_numidiv(L, nb, nc)); + } + else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_IDIV)); } + ) vmcase(OP_POW, TValue *rb = RKB(i); TValue *rc = RKC(i); -- cgit v1.2.3-55-g6feb