diff --git a/src/bignum.h b/src/bignum.h index 4e4f326216ebc3f2e8486a790bd14f5305acb65a..cfbb024625928ba3860263777c4a553859ad685a 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -167,6 +167,16 @@ static INLINE int type ## _SUB_OVERFLOW(type a, type b) { \ int of = 0; \ DO_ ## type ## _SUB_OVERFLOW(a, b, &of); \ return of; \ +} \ +static INLINE int type ## _MOD_OVERFLOW(type a, type b) { \ + return type ## _DIV_OVERFLOW(a, b); \ +} \ +static INLINE type DO_ ## type ## _MOD_OVERFLOW(type a, type b, int * of) { \ + if (type ## _MOD_OVERFLOW(a, b)) { \ + *of = 1; \ + return 0; \ + } \ + return a % b; \ } diff --git a/src/operators.c b/src/operators.c index 7f94cb370ecc2c91408fe75bcbcf33c8d67ef9cf..32f1ca487b2aae391468d009fc7559cbf6b73f2b 100644 --- a/src/operators.c +++ b/src/operators.c @@ -4231,6 +4231,9 @@ PMOD_EXPORT void o_mod(void) { if(TYPEOF(sp[-2]) != TYPEOF(sp[-1]) && !float_promote()) { +#ifdef AUTO_BIGNUM +do_lfun_modulo: +#endif if(call_lfun(LFUN_MOD, LFUN_RMOD)) return; @@ -4305,28 +4308,49 @@ PMOD_EXPORT void o_mod(void) return; } case T_INT: - if (sp[-1].u.integer == 0) + { + int of = 0; + INT_TYPE a = sp[-2].u.integer, + b = sp[-1].u.integer; + INT_TYPE res; + if (b == 0) OP_MODULO_BY_ZERO_ERROR("`%"); - sp--; - if(sp[-1].u.integer>=0) + if(a>=0) { - if(sp[0].u.integer>=0) + if(b>=0) { - sp[-1].u.integer %= sp[0].u.integer; + res = a % b; }else{ - sp[-1].u.integer=((sp[-1].u.integer+~sp[0].u.integer)%-sp[0].u.integer)-~sp[0].u.integer; + /* res = ((a+~b)%-b)-~b */ + res = DO_INT_TYPE_ADD_OVERFLOW(a, ~b, &of); + res = DO_INT_TYPE_MOD_OVERFLOW(res, b, &of); + res = DO_INT_TYPE_SUB_OVERFLOW(res, ~b, &of); } }else{ - if(sp[0].u.integer>=0) + if(b>=0) { - sp[-1].u.integer=sp[0].u.integer+~((~sp[-1].u.integer) % sp[0].u.integer); + /* res = b+~((~a) % b) */ + res = DO_INT_TYPE_MOD_OVERFLOW(~a, b, &of); + res = DO_INT_TYPE_ADD_OVERFLOW(b, ~res, &of); }else{ - sp[-1].u.integer=-(-sp[-1].u.integer % -sp[0].u.integer); + /* a % b and a % -b are equivalent, if overflow does not + * happen + * res = -(-a % -b) = a % b; */ + res = DO_INT_TYPE_MOD_OVERFLOW(a, b, &of); } } - SET_SVAL_SUBTYPE(sp[-1], NUMBER_NUMBER); +#ifdef AUTO_BIGNUM + if (of) { + stack_swap(); + convert_stack_top_to_bignum(); + stack_swap(); + goto do_lfun_modulo; + } +#endif + sp--; + SET_SVAL(sp[-1], T_INT, NUMBER_NUMBER, integer, res); return; - + } default: PIKE_ERROR("`%", "Bad argument 1.\n", sp, 2); } diff --git a/src/testsuite.in b/src/testsuite.in index f03d6f84fa827355194ad71354190affc1028714..e6a656d430858ba319cf09d3d1a70f7c38105a3e 100644 --- a/src/testsuite.in +++ b/src/testsuite.in @@ -8374,6 +8374,8 @@ test_eq(0 ? "a" : 0 ? "b" : 1 ? "c" : 1, "c") // testing overflow checks test_eq(-1 - 0x7fffffff, -0x80000000) +test_eq(Int.NATIVE_MIN % -1, 0) +test_eq(Int.NATIVE_MAX % Int.NATIVE_MIN, -1) // testing indexing test_eq("foo"[0],'f')