Skip to content

Commit b318ebf

Browse files
honza-kludpgeorge
authored andcommitted
py/modbuiltins: Add support for rounding integers.
As per CPython semantics. This feature is controlled by MICROPY_PY_BUILTINS_ROUND_INT which is disabled by default.
1 parent f2ec792 commit b318ebf

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

py/modbuiltins.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,36 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr);
445445
STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) {
446446
mp_obj_t o_in = args[0];
447447
if (MP_OBJ_IS_INT(o_in)) {
448-
return o_in;
448+
if (n_args <= 1) {
449+
return o_in;
450+
}
451+
452+
#if !MICROPY_PY_BUILTINS_ROUND_INT
453+
mp_raise_NotImplementedError(NULL);
454+
#else
455+
mp_int_t num_dig = mp_obj_get_int(args[1]);
456+
if (num_dig >= 0) {
457+
return o_in;
458+
}
459+
460+
mp_obj_t mult = mp_binary_op(MP_BINARY_OP_POWER, MP_OBJ_NEW_SMALL_INT(10), MP_OBJ_NEW_SMALL_INT(-num_dig));
461+
mp_obj_t half_mult = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, mult, MP_OBJ_NEW_SMALL_INT(2));
462+
mp_obj_t modulo = mp_binary_op(MP_BINARY_OP_MODULO, o_in, mult);
463+
mp_obj_t rounded = mp_binary_op(MP_BINARY_OP_SUBTRACT, o_in, modulo);
464+
if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, half_mult, modulo))) {
465+
return rounded;
466+
} else if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, modulo, half_mult))) {
467+
return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
468+
} else {
469+
// round to even number
470+
mp_obj_t floor = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, o_in, mult);
471+
if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_AND, floor, MP_OBJ_NEW_SMALL_INT(1)))) {
472+
return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
473+
} else {
474+
return rounded;
475+
}
476+
}
477+
#endif
449478
}
450479
#if MICROPY_PY_BUILTINS_FLOAT
451480
mp_float_t val = mp_obj_get_float(o_in);

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ typedef double mp_float_t;
802802
#define MICROPY_PY_BUILTINS_RANGE_BINOP (0)
803803
#endif
804804

805+
// Whether to support rounding of integers (incl bignum); eg round(123,-1)=120
806+
#ifndef MICROPY_PY_BUILTINS_ROUND_INT
807+
#define MICROPY_PY_BUILTINS_ROUND_INT (0)
808+
#endif
809+
805810
// Whether to support timeout exceptions (like socket.timeout)
806811
#ifndef MICROPY_PY_BUILTINS_TIMEOUTERROR
807812
#define MICROPY_PY_BUILTINS_TIMEOUTERROR (0)

tests/basics/builtin_round_int.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# test round() with integer values and second arg
2+
3+
# rounding integers is an optional feature so test for it
4+
try:
5+
round(1, -1)
6+
except NotImplementedError:
7+
print('SKIP')
8+
raise SystemExit
9+
10+
tests = [
11+
(1, False), (1, True),
12+
(124, -1), (125, -1), (126, -1),
13+
(5, -1), (15, -1), (25, -1),
14+
(12345, 0), (12345, -1), (12345, 1),
15+
(-1234, 0), (-1234, -1), (-1234, 1),
16+
]
17+
for t in tests:
18+
print(round(*t))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# test round() with large integer values and second arg
2+
3+
# rounding integers is an optional feature so test for it
4+
try:
5+
round(1, -1)
6+
except NotImplementedError:
7+
print('SKIP')
8+
raise SystemExit
9+
10+
i = 2**70
11+
12+
tests = [
13+
(i, 0), (i, -1), (i, -10), (i, 1),
14+
(-i, 0), (-i, -1), (-i, -10), (-i, 1),
15+
]
16+
for t in tests:
17+
print(round(*t))

0 commit comments

Comments
 (0)