Integer operation should result in Integer.

Should raise `RangeError` if the operation overflows.
parent 600e3330
...@@ -46,6 +46,12 @@ mrb_to_flo(mrb_state *mrb, mrb_value val) ...@@ -46,6 +46,12 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
} }
#endif #endif
static void
int_overflow(mrb_state *mrb, const char *reason)
{
mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason);
}
/* /*
* call-seq: * call-seq:
* *
...@@ -88,13 +94,13 @@ int_pow(mrb_state *mrb, mrb_value x) ...@@ -88,13 +94,13 @@ int_pow(mrb_state *mrb, mrb_value x)
for (;;) { for (;;) {
if (exp & 1) { if (exp & 1) {
if (mrb_int_mul_overflow(result, base, &result)) { if (mrb_int_mul_overflow(result, base, &result)) {
mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in division"); int_overflow(mrb, "multiplication");
} }
} }
exp >>= 1; exp >>= 1;
if (exp == 0) break; if (exp == 0) break;
if (mrb_int_mul_overflow(base, base, &base)) { if (mrb_int_mul_overflow(base, base, &base)) {
mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in division"); int_overflow(mrb, "multiplication");
} }
} }
return mrb_int_value(mrb, result); return mrb_int_value(mrb, result);
...@@ -109,7 +115,7 @@ mrb_num_div_int(mrb_state *mrb, mrb_int x, mrb_int y) ...@@ -109,7 +115,7 @@ mrb_num_div_int(mrb_state *mrb, mrb_int x, mrb_int y)
mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0"); mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0");
} }
else if(x == MRB_INT_MIN && y == -1) { else if(x == MRB_INT_MIN && y == -1) {
mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in division"); int_overflow(mrb, "division");
} }
else { else {
mrb_int div, mod; mrb_int div, mod;
...@@ -921,9 +927,7 @@ fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) ...@@ -921,9 +927,7 @@ fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
if (a == 0) return x; if (a == 0) return x;
b = mrb_integer(y); b = mrb_integer(y);
if (mrb_int_mul_overflow(a, b, &c)) { if (mrb_int_mul_overflow(a, b, &c)) {
#ifndef MRB_NO_FLOAT int_overflow(mrb, "multiplication");
return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b);
#endif
} }
return mrb_fixnum_value(c); return mrb_fixnum_value(c);
} }
...@@ -1198,54 +1202,27 @@ int_xor(mrb_state *mrb, mrb_value x) ...@@ -1198,54 +1202,27 @@ int_xor(mrb_state *mrb, mrb_value x)
static mrb_value static mrb_value
lshift(mrb_state *mrb, mrb_int val, mrb_int width) lshift(mrb_state *mrb, mrb_int val, mrb_int width)
{ {
if (width < 0) { /* mrb_int overflow */ mrb_assert(width >= 0);
#ifdef MRB_NO_FLOAT
return mrb_fixnum_value(0);
#else
return mrb_float_value(mrb, INFINITY);
#endif
}
if (val > 0) { if (val > 0) {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) || if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val > (MRB_INT_MAX >> width))) { (val > (MRB_INT_MAX >> width))) {
#ifdef MRB_NO_FLOAT int_overflow(mrb, "bit shift");
return mrb_fixnum_value(-1);
#else
goto bit_overflow;
#endif
} }
return mrb_int_value(mrb, val << width); return mrb_int_value(mrb, val << width);
} }
else { else {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) || if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val <= (MRB_INT_MIN >> width))) { (val <= (MRB_INT_MIN >> width))) {
#ifdef MRB_NO_FLOAT int_overflow(mrb, "bit shift");
return mrb_fixnum_value(0);
#else
goto bit_overflow;
#endif
} }
return mrb_int_value(mrb, (val * ((mrb_int)1 << width))); return mrb_int_value(mrb, (val * ((mrb_int)1 << width)));
} }
#ifndef MRB_NO_FLOAT
bit_overflow:
{
mrb_float f = (mrb_float)val;
while (width--) {
f *= 2;
}
return mrb_float_value(mrb, f);
}
#endif
} }
static mrb_value static mrb_value
rshift(mrb_state *mrb, mrb_int val, mrb_int width) rshift(mrb_state *mrb, mrb_int val, mrb_int width)
{ {
if (width < 0) { /* mrb_int overflow */ mrb_assert(width >= 0);
return mrb_fixnum_value(0);
}
if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
if (val < 0) { if (val < 0) {
return mrb_fixnum_value(-1); return mrb_fixnum_value(-1);
...@@ -1275,6 +1252,7 @@ int_lshift(mrb_state *mrb, mrb_value x) ...@@ -1275,6 +1252,7 @@ int_lshift(mrb_state *mrb, mrb_value x)
val = mrb_integer(x); val = mrb_integer(x);
if (val == 0) return x; if (val == 0) return x;
if (width < 0) { if (width < 0) {
if (width == MRB_INT_MIN) return rshift(mrb, val, MRB_INT_BIT);
return rshift(mrb, val, -width); return rshift(mrb, val, -width);
} }
return lshift(mrb, val, width); return lshift(mrb, val, width);
...@@ -1300,6 +1278,7 @@ int_rshift(mrb_state *mrb, mrb_value x) ...@@ -1300,6 +1278,7 @@ int_rshift(mrb_state *mrb, mrb_value x)
val = mrb_integer(x); val = mrb_integer(x);
if (val == 0) return x; if (val == 0) return x;
if (width < 0) { if (width < 0) {
if (width == MRB_INT_MIN) int_overflow(mrb, "bit shift");
return lshift(mrb, val, -width); return lshift(mrb, val, -width);
} }
return rshift(mrb, val, width); return rshift(mrb, val, width);
...@@ -1371,9 +1350,7 @@ fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) ...@@ -1371,9 +1350,7 @@ fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
if (a == 0) return y; if (a == 0) return y;
b = mrb_integer(y); b = mrb_integer(y);
if (mrb_int_add_overflow(a, b, &c)) { if (mrb_int_add_overflow(a, b, &c)) {
#ifndef MRB_NO_FLOAT int_overflow(mrb, "addition");
return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b);
#endif
} }
return mrb_int_value(mrb, c); return mrb_int_value(mrb, c);
} }
...@@ -1427,9 +1404,7 @@ fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) ...@@ -1427,9 +1404,7 @@ fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
b = mrb_integer(y); b = mrb_integer(y);
if (mrb_int_sub_overflow(a, b, &c)) { if (mrb_int_sub_overflow(a, b, &c)) {
#ifndef MRB_NO_FLOAT int_overflow(mrb, "subtraction");
return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b);
#endif
} }
return mrb_int_value(mrb, c); return mrb_int_value(mrb, c);
} }
......
...@@ -125,10 +125,6 @@ assert('Integer#<<', '15.2.8.3.12') do ...@@ -125,10 +125,6 @@ assert('Integer#<<', '15.2.8.3.12') do
assert_equal 23, 46 << -1 assert_equal 23, 46 << -1
skip unless Object.const_defined?(:Float) skip unless Object.const_defined?(:Float)
# Overflow to Integer
assert_float 9223372036854775808.0, 1 << 63
assert_float(-13835058055282163712.0, -3 << 62)
end end
assert('Integer#>>', '15.2.8.3.13') do assert('Integer#>>', '15.2.8.3.13') do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment