Skip to content

Commit e72be1b

Browse files
committed
py: Fix smallint modulo with negative arguments.
Addresses issue adafruit#927.
1 parent 5fc42a6 commit e72be1b

4 files changed

Lines changed: 34 additions & 35 deletions

File tree

py/builtin.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "qstr.h"
3434
#include "obj.h"
3535
#include "objstr.h"
36+
#include "smallint.h"
3637
#include "runtime0.h"
3738
#include "runtime.h"
3839
#include "builtin.h"
@@ -253,8 +254,8 @@ STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
253254
nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "division by zero"));
254255
}
255256
mp_obj_t args[2];
256-
args[0] = MP_OBJ_NEW_SMALL_INT(i1 / i2);
257-
args[1] = MP_OBJ_NEW_SMALL_INT(i1 % i2);
257+
args[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(i1, i2));
258+
args[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(i1, i2));
258259
return mp_obj_new_tuple(2, args);
259260
#if MICROPY_PY_BUILTINS_FLOAT
260261
} else if (MP_OBJ_IS_TYPE(o1_in, &mp_type_float) || MP_OBJ_IS_TYPE(o2_in, &mp_type_float)) {

py/smallint.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,23 @@ bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) {
5757
}
5858

5959
mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor) {
60-
mp_int_t lsign = (dividend >= 0) ? 1 :-1;
61-
mp_int_t rsign = (divisor >= 0) ? 1 :-1;
60+
// Python specs require that mod has same sign as second operand
6261
dividend %= divisor;
63-
if (lsign != rsign) {
62+
if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) {
6463
dividend += divisor;
6564
}
6665
return dividend;
6766
}
6867

6968
mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom) {
70-
mp_int_t lsign = num > 0 ? 1 : -1;
71-
mp_int_t rsign = denom > 0 ? 1 : -1;
72-
if (lsign == -1) {num *= -1;}
73-
if (rsign == -1) {denom *= -1;}
74-
if (lsign != rsign){
75-
return - ( num + denom - 1) / denom;
69+
if (num >= 0) {
70+
if (denom < 0) {
71+
num += -denom - 1;
72+
}
7673
} else {
77-
return num / denom;
74+
if (denom >= 0) {
75+
num += -denom + 1;
76+
}
7877
}
78+
return num / denom;
7979
}

tests/basics/int_divmod.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# test integer floor division and modulo
2+
3+
# test all combination of +/-/0 cases
4+
for i in range(-2, 3):
5+
for j in range(-4, 5):
6+
if j != 0:
7+
print(i, j, i // j, i % j, divmod(i, j))
8+
9+
# this tests compiler constant folding
10+
print(123 // 7, 123 % 7)
11+
print(-123 // 7, -123 % 7)
12+
print(123 // -7, 123 % -7)
13+
print(-123 // -7, -123 % -7)
14+
15+
# this tests bignum modulo
16+
a = 987654321987987987987987987987
17+
b = 19
18+
print(a % b)
19+
print(a % -b)
20+
print(-a % b)
21+
print(-a % -b)

tests/basics/modulo.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)