summaryrefslogtreecommitdiffhomepage
path: root/src/numeric.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/numeric.c')
-rw-r--r--src/numeric.c213
1 files changed, 123 insertions, 90 deletions
diff --git a/src/numeric.c b/src/numeric.c
index 2117fcc3c..be71f1185 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -52,14 +52,32 @@ static mrb_value
num_pow(mrb_state *mrb, mrb_value x)
{
mrb_value y;
- mrb_float d, yv;
+ mrb_float d;
mrb_get_args(mrb, "o", &y);
- yv = mrb_to_flo(mrb, y);
- d = pow(mrb_to_flo(mrb, x), yv);
- if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 &&
- (d < 0 || (d > 0 && (mrb_int)d > 0)))
- return mrb_fixnum_value((mrb_int)d);
+ if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) {
+ /* try ipow() */
+ mrb_int base = mrb_fixnum(x);
+ mrb_int exp = mrb_fixnum(y);
+ mrb_int result = 1;
+
+ if (exp < 0) goto float_pow;
+ for (;;) {
+ if (exp & 1) {
+ if (mrb_int_mul_overflow(result, base, &result)) {
+ goto float_pow;
+ }
+ }
+ exp >>= 1;
+ if (exp == 0) break;
+ if (mrb_int_mul_overflow(base, base, &base)) {
+ goto float_pow;
+ }
+ }
+ return mrb_fixnum_value(result);
+ }
+ float_pow:
+ d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y));
return mrb_float_value(mrb, d);
}
@@ -169,7 +187,12 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *
mrb_float mod;
if (y == 0.0) {
- div = INFINITY;
+ if (x == 0.0) {
+ div = NAN;
+ }
+ else {
+ div = INFINITY;
+ }
mod = NAN;
}
else {
@@ -371,7 +394,7 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
val *= 2;
}
}
- if (FIXABLE(val)) {
+ if (FIXABLE_FLOAT(val)) {
return mrb_fixnum_value((mrb_int)val);
}
return mrb_float_value(mrb, val);
@@ -408,13 +431,13 @@ flo_hash(mrb_state *mrb, mrb_value num)
mrb_float d;
char *c;
size_t i;
- int hash;
+ mrb_int hash;
d = (mrb_float)mrb_fixnum(num);
/* normalize -0.0 to 0.0 */
if (d == 0) d = 0.0;
c = (char*)&d;
- for (hash=0, i=0; i<sizeof(mrb_float);i++) {
+ for (hash=0,i=0; i<sizeof(mrb_float); i++) {
hash = (hash * 971) ^ (unsigned char)c[i];
}
if (hash < 0) hash = -hash;
@@ -476,6 +499,17 @@ flo_finite_p(mrb_state *mrb, mrb_value num)
return mrb_bool_value(isfinite(mrb_float(num)));
}
+void
+mrb_check_num_exact(mrb_state *mrb, mrb_float num)
+{
+ if (isinf(num)) {
+ mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
+ }
+ if (isnan(num)) {
+ mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+ }
+}
+
/* 15.2.9.3.10 */
/*
* call-seq:
@@ -494,7 +528,8 @@ flo_floor(mrb_state *mrb, mrb_value num)
{
mrb_float f = floor(mrb_float(num));
- if (!FIXABLE(f)) {
+ mrb_check_num_exact(mrb, f);
+ if (!FIXABLE_FLOAT(f)) {
return mrb_float_value(mrb, f);
}
return mrb_fixnum_value((mrb_int)f);
@@ -519,7 +554,8 @@ flo_ceil(mrb_state *mrb, mrb_value num)
{
mrb_float f = ceil(mrb_float(num));
- if (!FIXABLE(f)) {
+ mrb_check_num_exact(mrb, f);
+ if (!FIXABLE_FLOAT(f)) {
return mrb_float_value(mrb, f);
}
return mrb_fixnum_value((mrb_int)f);
@@ -566,14 +602,10 @@ flo_round(mrb_state *mrb, mrb_value num)
mrb_get_args(mrb, "|i", &ndigits);
number = mrb_float(num);
- if (isinf(number)) {
- if (0 < ndigits) return num;
- else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity");
- }
- if (isnan(number)) {
- if (0 < ndigits) return num;
- else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+ if (0 < ndigits && (isinf(number) || isnan(number))) {
+ return num;
}
+ mrb_check_num_exact(mrb, number);
f = 1.0;
i = ndigits >= 0 ? ndigits : -ndigits;
@@ -610,17 +642,6 @@ flo_round(mrb_state *mrb, mrb_value num)
return mrb_fixnum_value((mrb_int)number);
}
-void
-mrb_check_num_exact(mrb_state *mrb, mrb_float num)
-{
- if (isinf(num)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
- }
- if (isnan(num)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
- }
-}
-
/* 15.2.9.3.14 */
/* 15.2.9.3.15 */
/*
@@ -640,8 +661,8 @@ flo_truncate(mrb_state *mrb, mrb_value num)
if (f > 0.0) f = floor(f);
if (f < 0.0) f = ceil(f);
- if (!FIXABLE(f)) {
- mrb_check_num_exact(mrb, f);
+ mrb_check_num_exact(mrb, f);
+ if (!FIXABLE_FLOAT(f)) {
return mrb_float_value(mrb, f);
}
return mrb_fixnum_value((mrb_int)f);
@@ -795,8 +816,10 @@ fix_divmod(mrb_state *mrb, mrb_value x)
mrb_int div, mod;
if (mrb_fixnum(y) == 0) {
- return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY),
- mrb_float_value(mrb, NAN));
+ return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ?
+ mrb_float_value(mrb, NAN):
+ mrb_float_value(mrb, INFINITY)),
+ mrb_float_value(mrb, NAN));
}
fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod);
return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod));
@@ -806,7 +829,7 @@ fix_divmod(mrb_state *mrb, mrb_value x)
mrb_value a, b;
flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, (mrb_int)div);
+ a = mrb_float_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -822,7 +845,7 @@ flo_divmod(mrb_state *mrb, mrb_value x)
mrb_get_args(mrb, "o", &y);
flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, (mrb_int)div);
+ a = mrb_float_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -938,22 +961,24 @@ fix_xor(mrb_state *mrb, mrb_value x)
static mrb_value
lshift(mrb_state *mrb, mrb_int val, mrb_int width)
{
- mrb_assert(width > 0);
+ if (width < 0) { /* mrb_int overflow */
+ return mrb_float_value(mrb, INFINITY);
+ }
if (val > 0) {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val > (MRB_INT_MAX >> width))) {
goto bit_overflow;
}
+ return mrb_fixnum_value(val << width);
}
else {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val < (MRB_INT_MIN >> width))) {
goto bit_overflow;
}
+ return mrb_fixnum_value(val * (1u << width));
}
- return mrb_fixnum_value(val << width);
-
bit_overflow:
{
mrb_float f = (mrb_float)val;
@@ -967,7 +992,9 @@ bit_overflow:
static mrb_value
rshift(mrb_int val, mrb_int width)
{
- mrb_assert(width > 0);
+ if (width < 0) { /* mrb_int overflow */
+ return mrb_fixnum_value(0);
+ }
if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
if (val < 0) {
return mrb_fixnum_value(-1);
@@ -995,6 +1022,7 @@ fix_lshift(mrb_state *mrb, mrb_value x)
return x;
}
val = mrb_fixnum(x);
+ if (val == 0) return x;
if (width < 0) {
return rshift(val, -width);
}
@@ -1019,6 +1047,7 @@ fix_rshift(mrb_state *mrb, mrb_value x)
return x;
}
val = mrb_fixnum(x);
+ if (val == 0) return x;
if (width < 0) {
return lshift(mrb, val, -width);
}
@@ -1072,7 +1101,7 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
if (isnan(d)) {
mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
}
- if (FIXABLE(d)) {
+ if (FIXABLE_FLOAT(d)) {
z = (mrb_int)d;
}
else {
@@ -1276,68 +1305,72 @@ mrb_init_numeric(mrb_state *mrb)
/* Numeric Class */
numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */
- mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */
- mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
- mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
+ mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */
+ mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
/* Integer Class */
integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM);
mrb_undef_class_method(mrb, integer, "new");
- mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
- mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
+ mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, integer, "ceil", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
+ mrb_define_method(mrb, integer, "floor", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
+ mrb_define_method(mrb, integer, "round", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */
+ mrb_define_method(mrb, integer, "truncate", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */
/* Fixnum Class */
mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer);
- mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
- mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
- mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
- mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
- mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
- mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
- mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
- mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
- mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
- mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
- mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
- mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
- mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */
- mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */
- mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */
- mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE());
- mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
+ mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
+ mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
+ mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
+ mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
+ mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
+ mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
+ mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
+ mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
+ mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
+ mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
+ mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
+ mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+ mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */
+ mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */
+ mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */
+ mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
/* Float Class */
mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
mrb_undef_class_method(mrb, fl, "new");
- mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
- mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
- mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
- mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
- mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
- mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */
- mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
- mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */
- mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
- mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
- mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
- mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
- mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */
- mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
-
- mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
- mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
+ mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
+ mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
+ mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
+ mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
+ mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */
+ mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
+ mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */
+ mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
+ mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
+ mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
+ mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
+ mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */
+ mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+
+ mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
+ mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE());
#ifdef INFINITY
mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY));