diff options
Diffstat (limited to 'src/numeric.c')
| -rw-r--r-- | src/numeric.c | 274 |
1 files changed, 76 insertions, 198 deletions
diff --git a/src/numeric.c b/src/numeric.c index db1e7d0f4..7b49b29f7 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -9,25 +9,21 @@ #include <math.h> #include <stdlib.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/numeric.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/numeric.h> +#include <mruby/string.h> #ifdef MRB_USE_FLOAT #define floor(f) floorf(f) #define ceil(f) ceilf(f) #define fmod(x,y) fmodf(x,y) -#define FLO_MAX_DIGITS 7 -#define FLO_MAX_SIGN_LENGTH 3 -#define FLO_EPSILON FLT_EPSILON +#define MRB_FLO_TO_STR_FMT "%.7g" #else -#define FLO_MAX_DIGITS 14 -#define FLO_MAX_SIGN_LENGTH 10 -#define FLO_EPSILON DBL_EPSILON +#define MRB_FLO_TO_STR_FMT "%.14g" #endif -mrb_float +MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value val) { switch (mrb_type(val)) { @@ -54,13 +50,13 @@ static mrb_value num_pow(mrb_state *mrb, mrb_value x) { mrb_value y; - mrb_bool both_int = FALSE; - mrb_float d; + mrb_float d, yv; mrb_get_args(mrb, "o", &y); - if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) both_int = TRUE; - d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); - if (both_int && FIXABLE(d)) + 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); return mrb_float_value(mrb, d); } @@ -108,142 +104,6 @@ num_div(mrb_state *mrb, mrb_value x) * representation. */ -static mrb_value -mrb_flo_to_str(mrb_state *mrb, mrb_float flo) -{ - double n = (double)flo; - int max_digits = FLO_MAX_DIGITS; - - if (isnan(n)) { - return mrb_str_new_lit(mrb, "NaN"); - } - else if (isinf(n)) { - if (n < 0) { - return mrb_str_new_lit(mrb, "-inf"); - } - else { - return mrb_str_new_lit(mrb, "inf"); - } - } - else { - int digit; - int m = 0; - int exp; - mrb_bool e = FALSE; - char s[48]; - char *c = &s[0]; - int length = 0; - - if (signbit(n)) { - n = -n; - *(c++) = '-'; - } - - if (n != 0.0) { - if (n > 1.0) { - exp = (int)floor(log10(n)); - } - else { - exp = (int)-ceil(-log10(n)); - } - } - else { - exp = 0; - } - - /* preserve significands */ - if (exp < 0) { - int i, beg = -1, end = 0; - double f = n; - double fd = 0; - for (i = 0; i < FLO_MAX_DIGITS; ++i) { - f = (f - fd) * 10.0; - fd = floor(f + FLO_EPSILON); - if (fd != 0) { - if (beg < 0) beg = i; - end = i + 1; - } - } - if (beg >= 0) length = end - beg; - if (length > FLO_MAX_SIGN_LENGTH) length = FLO_MAX_SIGN_LENGTH; - } - - if (abs(exp) + length >= FLO_MAX_DIGITS) { - /* exponent representation */ - e = TRUE; - n = n / pow(10.0, exp); - if (isinf(n)) { - if (s < c) { /* s[0] == '-' */ - return mrb_str_new_lit(mrb, "-0.0"); - } - else { - return mrb_str_new_lit(mrb, "0.0"); - } - } - } - else { - /* un-exponent (normal) representation */ - if (exp > 0) { - m = exp; - } - } - - /* puts digits */ - while (max_digits >= 0) { - double weight = pow(10.0, m); - double fdigit = n / weight; - - if (fdigit < 0) fdigit = n = 0; - if (m < -1 && fdigit < FLO_EPSILON) { - if (e || exp > 0 || m <= -abs(exp)) { - break; - } - } - digit = (int)floor(fdigit + FLO_EPSILON); - if (m == 0 && digit > 9) { - n /= 10.0; - exp++; - continue; - } - *(c++) = '0' + digit; - n -= (digit * weight); - max_digits--; - if (m-- == 0) { - *(c++) = '.'; - } - } - if (c[-1] == '0') { - while (&s[0] < c && c[-1] == '0') { - c--; - } - c++; - } - - if (e) { - *(c++) = 'e'; - if (exp > 0) { - *(c++) = '+'; - } - else { - *(c++) = '-'; - exp = -exp; - } - - if (exp >= 100) { - *(c++) = '0' + exp / 100; - exp -= exp / 100 * 100; - } - - *(c++) = '0' + exp / 10; - *(c++) = '0' + exp % 10; - } - - *c = '\0'; - - return mrb_str_new(mrb, &s[0], c - &s[0]); - } -} - /* 15.2.9.3.16(x) */ /* * call-seq: @@ -251,14 +111,17 @@ mrb_flo_to_str(mrb_state *mrb, mrb_float flo) * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return - * ``<code>NaN</code>'', ``<code>Infinity</code>'', and - * ``<code>-Infinity</code>''. + * "<code>NaN</code>", "<code>Infinity</code>", and + * "<code>-Infinity</code>". */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { - return mrb_flo_to_str(mrb, mrb_float(flt)); + if (isnan(mrb_float(flt))) { + return mrb_str_new_lit(mrb, "NaN"); + } + return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT); } /* 15.2.9.3.2 */ @@ -339,12 +202,11 @@ static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { mrb_value y; - mrb_float fy, mod; + mrb_float mod; mrb_get_args(mrb, "o", &y); - fy = mrb_to_flo(mrb, y); - flodivmod(mrb, mrb_float(x), fy, 0, &mod); + flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } @@ -397,22 +259,16 @@ static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { mrb_value y; - volatile mrb_float a, b; - mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: - b = (mrb_float)mrb_fixnum(y); - break; + return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); case MRB_TT_FLOAT: - b = mrb_float(y); - break; + return mrb_bool_value(mrb_float(x) == mrb_float(y)); default: return mrb_false_value(); } - a = mrb_float(x); - return mrb_bool_value(a == b); } /* 15.2.8.3.18 */ @@ -474,7 +330,7 @@ flo_infinite_p(mrb_state *mrb, mrb_value num) mrb_float value = mrb_float(num); if (isinf(value)) { - return mrb_fixnum_value( value < 0 ? -1 : 1 ); + return mrb_fixnum_value(value < 0 ? -1 : 1); } return mrb_nil_value(); } @@ -493,9 +349,7 @@ flo_infinite_p(mrb_state *mrb, mrb_value num) static mrb_value flo_finite_p(mrb_state *mrb, mrb_value num) { - mrb_float value = mrb_float(num); - - return mrb_bool_value(isfinite(value)); + return mrb_bool_value(isfinite(mrb_float(num))); } /* 15.2.9.3.10 */ @@ -583,7 +437,7 @@ flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; - int i; + mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); @@ -598,7 +452,7 @@ flo_round(mrb_state *mrb, mrb_value num) } f = 1.0; - i = abs(ndigits); + i = ndigits >= 0 ? ndigits : -ndigits; while (--i >= 0) f = f*10.0; @@ -613,12 +467,12 @@ flo_round(mrb_state *mrb, mrb_value num) /* home-made inline implementation of round(3) */ if (number > 0.0) { - d = floor(number); - number = d + (number - d >= 0.5); + d = floor(number); + number = d + (number - d >= 0.5); } else if (number < 0.0) { - d = ceil(number); - number = d - (d - number >= 0.5); + d = ceil(number); + number = d - (d - number >= 0.5); } if (ndigits < 0) number *= f; @@ -687,12 +541,8 @@ int_to_i(mrb_state *mrb, mrb_value num) return num; } -#ifdef MRB_FIXNUM_SHIFT -#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) -#else -#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1)/2)) -#endif /*tests if N*N would overflow*/ +#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) #define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX)) mrb_value @@ -713,7 +563,7 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) if ((a != 0 && c/a != b) || !FIXABLE(c)) { return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b); } - return mrb_fixnum_value(c); + return mrb_fixnum_value((mrb_int)c); } return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); } @@ -970,19 +820,35 @@ 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 > NUMERIC_SHIFT_WIDTH_MAX) { - mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)", - mrb_fixnum_value(width), - mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); + mrb_assert(width > 0); + if (val > 0) { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val > (MRB_INT_MAX >> width))) { + goto bit_overflow; + } + } else { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val < (MRB_INT_MIN >> width))) { + goto bit_overflow; + } } + return mrb_fixnum_value(val << width); + +bit_overflow: + { + mrb_float f = (mrb_float)val; + while (width--) { + f *= 2; + } + return mrb_float_value(mrb, f); + } } static mrb_value rshift(mrb_int val, mrb_int width) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); @@ -1004,7 +870,7 @@ fix_shift_get_width(mrb_state *mrb, mrb_int *width) /* 15.2.8.3.12 */ /* * call-seq: - * fix << count -> integer + * fix << count -> integer or float * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ @@ -1029,7 +895,7 @@ fix_lshift(mrb_state *mrb, mrb_value x) /* 15.2.8.3.13 */ /* * call-seq: - * fix >> count -> integer + * fix >> count -> integer or float * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ @@ -1080,14 +946,14 @@ fix_to_f(mrb_state *mrb, mrb_value num) * FloatDomainError: Infinity */ /* ------------------------------------------------------------------------*/ -mrb_value +MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) { mrb_int z; - if (mrb_float_p(x)) { - mrb_raise(mrb, E_TYPE_ERROR, "non float value"); - z = 0; /* not reached. just suppress warnings. */ + if (!mrb_float_p(x)) { + mrb_raise(mrb, E_TYPE_ERROR, "non float value"); + z = 0; /* not reached. just suppress warnings. */ } else { mrb_float d = mrb_float(x); @@ -1098,7 +964,12 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) if (isnan(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } - z = (mrb_int)d; + if (FIXABLE(d)) { + z = (mrb_int)d; + } + else { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); + } } return mrb_fixnum_value(z); } @@ -1178,7 +1049,7 @@ fix_minus(mrb_state *mrb, mrb_value self) } -mrb_value +MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base) { char buf[MRB_INT_BIT+1]; @@ -1309,7 +1180,7 @@ mrb_init_numeric(mrb_state *mrb) mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); /* Fixnum Class */ - fixnum = mrb->fixnum_class = mrb_define_class(mrb, "Fixnum", integer); + 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 */ @@ -1329,7 +1200,7 @@ mrb_init_numeric(mrb_state *mrb) mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ /* Float Class */ - fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ + mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ 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 */ @@ -1351,4 +1222,11 @@ mrb_init_numeric(mrb_state *mrb) 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)); +#endif +#ifdef NAN + mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN)); +#endif } |
