diff --git a/src/numeric.c b/src/numeric.c index be9a26dda..811a7c0b1 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -1565,29 +1565,94 @@ int_rshift(mrb_state *mrb, mrb_value x) return mrb_int_value(mrb, val); } +static mrb_value +int_rounding(mrb_state *mrb, mrb_value x, double (*func)(double)) +{ + mrb_int nd = 0; + mrb_get_args(mrb, "|i", &nd); + if (nd >= 0) { + return x; + } + mrb_float f = mrb_as_float(mrb, x); + mrb_float d = pow(10, -(double)nd); + f = func(f / d) * d; + if (!FIXABLE_FLOAT(f)) { +#ifdef MRB_USE_BIGINT + return mrb_bint_new_float(mrb, f); +#else + mrb_int_overflow(mrb, "rounding"); +#endif + } + return mrb_int_value(mrb, (mrb_int)f); +} + /* 15.2.8.3.14 Integer#ceil */ -/* 15.2.8.3.17 Integer#floor */ -/* 15.2.8.3.20 Integer#round */ -/* 15.2.8.3.26 Integer#truncate */ /* * call-seq: - * int.ceil -> int - * int.floor -> int - * int.round -> int - * int.truncate -> int + * int.ceil -> int + * int.ceil(ndigits) -> int * * Returns self. + * + * When the precision (ndigits) is negative, the returned value is an integer + * with at least ndigits.abs trailing zeros. */ - static mrb_value int_ceil(mrb_state *mrb, mrb_value x) { - return x; + return int_rounding(mrb, x, ceil); } -#define int_floor int_ceil -#define int_round int_ceil -#define int_truncate int_ceil +/* 15.2.8.3.17 Integer#floor */ +/* + * call-seq: + * int.floor -> int + * int.floor(ndigits) -> int + * + * Returns self. + * + * When the precision (ndigits) is negative, the returned value is an integer + * with at least ndigits.abs trailing zeros. + */ +static mrb_value +int_floor(mrb_state *mrb, mrb_value x) +{ + return int_rounding(mrb, x, floor); +} + +/* 15.2.8.3.20 Integer#round */ +/* + * call-seq: + * int.round -> int + * int.round(ndigits) -> int + * + * Returns self. + * + * When the precision (ndigits) is negative, the returned value is an integer + * with at least ndigits.abs trailing zeros. + */ +static mrb_value +int_round(mrb_state *mrb, mrb_value x) +{ + return int_rounding(mrb, x, round); +} + +/* 15.2.8.3.26 Integer#truncate */ +/* + * call-seq: + * int.truncate -> int + * int.truncate(ndigits) -> int + * + * Returns self. + * + * When the precision (ndigits) is negative, the returned value is an integer + * with at least ndigits.abs trailing zeros. + */ +static mrb_value +int_truncate(mrb_state *mrb, mrb_value x) +{ + return int_rounding(mrb, x, trunc); +} /* 15.2.8.3.23 */ /*