numeric.c (flo_to_i): use bigint if the value is bigger than mrb_int.

This commit is contained in:
Yukihiro "Matz" Matsumoto
2022-04-18 11:35:15 +09:00
parent 96ff3c31c4
commit b38a766427
3 changed files with 53 additions and 0 deletions
+1
View File
@@ -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);
+48
View File
@@ -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)
{
+4
View File
@@ -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);