diff --git a/include/mruby/internal.h b/include/mruby/internal.h index 9ce87c8aa..be71f0799 100644 --- a/include/mruby/internal.h +++ b/include/mruby/internal.h @@ -181,6 +181,7 @@ mrb_value mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width); mrb_value mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width); mrb_value mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base); #ifndef MRB_NO_FLOAT +mrb_value mrb_bint_new_float(mrb_state *mrb, mrb_float x); mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value x); #endif mrb_int mrb_bint_as_int(mrb_state *mrb, mrb_value x); diff --git a/src/bigint.c b/src/bigint.c index 2ac5130c6..2f29d9fc5 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -1011,6 +1011,54 @@ mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x) } #ifndef MRB_NO_FLOAT +mrb_value +mrb_bint_new_float(mrb_state *mrb, mrb_float x) +{ + /* x should not be NaN nor Infinity */ + mrb_assert(x == x && x != x * 0.5); + + if (x < 1.0) { + return mrb_fixnum_value(0); + } + + struct RBigint *bint = bint_new(mrb); + mpz_t *r = &bint->mp; + + if (x < 0.0) { + x = -x; + r->sn = -1; + } + else { + r->sn = 1; + } + + mrb_float b = (double)CMASK; + mrb_float bi = 1.0 / b; + size_t rn; + int i; + mp_limb *rp; + mp_limb f; + + for (rn = 1; x >= b; rn++) + x *= bi; + + mpz_realloc(mrb, r, rn); + rp = r->p; + f = (mp_limb)x; + x -= f; + mrb_assert(x < 1.0); + i = rn-1; + rp[i] = f; + while (--i >= 0) { + x = b * x; + f = (mp_limb)x; + x -= f; + mrb_assert(x < 1.0); + rp[i] = f; + } + return bint_norm(mrb, bint); +} + mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value self) { diff --git a/src/numeric.c b/src/numeric.c index e33f1bed7..60250ea32 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -1014,7 +1014,11 @@ flo_to_i(mrb_state *mrb, mrb_value num) mrb_check_num_exact(mrb, f); if (!FIXABLE_FLOAT(f)) { +#ifdef MRB_USE_BIGINT + return mrb_bint_new_float(mrb, f); +#else mrb_int_overflow(mrb, "to_f"); +#endif } if (f > 0.0) f = floor(f); if (f < 0.0) f = ceil(f);