From e72be1b999a3337f74a8e5a8f61705666f834d2b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Oct 2014 23:05:50 +0100 Subject: [PATCH] py: Fix smallint modulo with negative arguments. Addresses issue #927. --- py/builtin.c | 5 +++-- py/smallint.c | 20 ++++++++++---------- tests/basics/int_divmod.py | 21 +++++++++++++++++++++ tests/basics/modulo.py | 23 ----------------------- 4 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 tests/basics/int_divmod.py delete mode 100644 tests/basics/modulo.py diff --git a/py/builtin.c b/py/builtin.c index 1af92a448..133473d01 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -33,6 +33,7 @@ #include "qstr.h" #include "obj.h" #include "objstr.h" +#include "smallint.h" #include "runtime0.h" #include "runtime.h" #include "builtin.h" @@ -253,8 +254,8 @@ STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "division by zero")); } mp_obj_t args[2]; - args[0] = MP_OBJ_NEW_SMALL_INT(i1 / i2); - args[1] = MP_OBJ_NEW_SMALL_INT(i1 % i2); + args[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(i1, i2)); + args[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(i1, i2)); return mp_obj_new_tuple(2, args); #if MICROPY_PY_BUILTINS_FLOAT } else if (MP_OBJ_IS_TYPE(o1_in, &mp_type_float) || MP_OBJ_IS_TYPE(o2_in, &mp_type_float)) { diff --git a/py/smallint.c b/py/smallint.c index 804514799..3a06c40a9 100644 --- a/py/smallint.c +++ b/py/smallint.c @@ -57,23 +57,23 @@ bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) { } mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor) { - mp_int_t lsign = (dividend >= 0) ? 1 :-1; - mp_int_t rsign = (divisor >= 0) ? 1 :-1; + // Python specs require that mod has same sign as second operand dividend %= divisor; - if (lsign != rsign) { + if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) { dividend += divisor; } return dividend; } mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom) { - mp_int_t lsign = num > 0 ? 1 : -1; - mp_int_t rsign = denom > 0 ? 1 : -1; - if (lsign == -1) {num *= -1;} - if (rsign == -1) {denom *= -1;} - if (lsign != rsign){ - return - ( num + denom - 1) / denom; + if (num >= 0) { + if (denom < 0) { + num += -denom - 1; + } } else { - return num / denom; + if (denom >= 0) { + num += -denom + 1; + } } + return num / denom; } diff --git a/tests/basics/int_divmod.py b/tests/basics/int_divmod.py new file mode 100644 index 000000000..71a990823 --- /dev/null +++ b/tests/basics/int_divmod.py @@ -0,0 +1,21 @@ +# test integer floor division and modulo + +# test all combination of +/-/0 cases +for i in range(-2, 3): + for j in range(-4, 5): + if j != 0: + print(i, j, i // j, i % j, divmod(i, j)) + +# this tests compiler constant folding +print(123 // 7, 123 % 7) +print(-123 // 7, -123 % 7) +print(123 // -7, 123 % -7) +print(-123 // -7, -123 % -7) + +# this tests bignum modulo +a = 987654321987987987987987987987 +b = 19 +print(a % b) +print(a % -b) +print(-a % b) +print(-a % -b) diff --git a/tests/basics/modulo.py b/tests/basics/modulo.py deleted file mode 100644 index c95305d13..000000000 --- a/tests/basics/modulo.py +++ /dev/null @@ -1,23 +0,0 @@ -# check modulo matches python definition - -# this tests compiler constant folding -print(123 % 7) -print(-123 % 7) -print(123 % -7) -print(-123 % -7) - -a = 321 -b = 19 -print(a % b) -print(a % -b) -print(-a % b) -print(-a % -b) - - -a = 987654321987987987987987987987 -b = 19 - -print(a % b) -print(a % -b) -print(-a % b) -print(-a % -b)