expt: avoid undefined integer overflow in fixnum implementation
Also, extend fixnum fast path to work more often in 64-bit mode. More review would be appreciated to help ensure that the revised implementation avoids undefined behavior in C.
This commit is contained in:
parent
58b898bc13
commit
e96d592735
|
@ -50,9 +50,11 @@
|
||||||
#ifdef SIXTY_FOUR_BIT_INTEGERS
|
#ifdef SIXTY_FOUR_BIT_INTEGERS
|
||||||
# define MAX_SHIFT_TRY 61
|
# define MAX_SHIFT_TRY 61
|
||||||
# define MAX_SHIFT_EVER 64
|
# define MAX_SHIFT_EVER 64
|
||||||
|
# define MAX_FIXNUM_SQRT 3037000498
|
||||||
#else
|
#else
|
||||||
# define MAX_SHIFT_TRY 29
|
# define MAX_SHIFT_TRY 29
|
||||||
# define MAX_SHIFT_EVER 32
|
# define MAX_SHIFT_EVER 32
|
||||||
|
# define MAX_FIXNUM_SQRT 46339
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* locals */
|
/* locals */
|
||||||
|
@ -3217,31 +3219,36 @@ static Scheme_Object *fixnum_expt(intptr_t x, intptr_t y)
|
||||||
|
|
||||||
if ((x == 2) && (y <= MAX_SHIFT_TRY))
|
if ((x == 2) && (y <= MAX_SHIFT_TRY))
|
||||||
return scheme_make_integer((intptr_t)1 << y);
|
return scheme_make_integer((intptr_t)1 << y);
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
intptr_t result = 1;
|
intptr_t result = 1;
|
||||||
int odd_result = (x < 0) && (y & 0x1);
|
int neg_result = (x < 0) && (y & 0x1);
|
||||||
|
|
||||||
if (x < 0)
|
if (x < 0)
|
||||||
x = -x;
|
x = -x;
|
||||||
while (y > 0)
|
|
||||||
{
|
while (y > 0) {
|
||||||
/* x^y*result is invariant and result <= x */
|
/* x^y*result is invariant and result <= x */
|
||||||
if (x > 46339 && y > 1) /* x * x won't fit in 31 bits */
|
if (x > MAX_FIXNUM_SQRT && y > 1) /* x * x won't fit in fixnum */
|
||||||
return scheme_generic_integer_power(scheme_make_integer_value(orig_x), scheme_make_integer_value(orig_y));
|
return scheme_generic_integer_power(scheme_make_integer_value(orig_x),
|
||||||
|
scheme_make_integer_value(orig_y));
|
||||||
|
|
||||||
if (y & 0x1) /* if (odd?) */
|
if (y & 0x1) /* if (odd?) */
|
||||||
{
|
{
|
||||||
intptr_t next_result = x * result;
|
uintptr_t next_result = (uintptr_t)x * (uintptr_t)result;
|
||||||
if (y == 1 && x > 46339 && !(next_result / x == result))
|
if ((y == 1)
|
||||||
return scheme_generic_integer_power(scheme_make_integer_value(orig_x), scheme_make_integer_value(orig_y));
|
&& (x > MAX_FIXNUM_SQRT)
|
||||||
|
&& (((intptr_t)next_result < 0)
|
||||||
|
|| !(next_result / (uintptr_t)x == (uintptr_t)result)))
|
||||||
|
return scheme_generic_integer_power(scheme_make_integer_value(orig_x),
|
||||||
|
scheme_make_integer_value(orig_y));
|
||||||
else
|
else
|
||||||
result = next_result;
|
result = (intptr_t)next_result;
|
||||||
}
|
}
|
||||||
y = y >> 1;
|
y = y >> 1;
|
||||||
x = x * x;
|
x = x * x;
|
||||||
}
|
}
|
||||||
return scheme_make_integer_value(odd_result ? -result : result);
|
|
||||||
|
return scheme_make_integer_value(neg_result ? -result : result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user