From 8269ab2e1fd073f2970535ccd796213db29cd2bd Mon Sep 17 00:00:00 2001 From: nobu Date: Wed, 2 May 2007 08:12:31 +0000 Subject: * bignum.c (rb_big_quo): now calculate in integer. [ruby-dev:30753] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12242 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- bignum.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 6 deletions(-) (limited to 'bignum.c') diff --git a/bignum.c b/bignum.c index a551a56803..b17fc4e9d8 100644 --- a/bignum.c +++ b/bignum.c @@ -13,6 +13,7 @@ #include "ruby.h" #include +#include #include #ifdef HAVE_IEEEFP_H #include @@ -852,8 +853,8 @@ rb_dbl2big(double d) return bignorm(dbl2big(d)); } -double -rb_big2dbl(VALUE x) +static double +big2dbl(VALUE x) { double d = 0.0; long i = RBIGNUM(x)->len; @@ -862,11 +863,19 @@ rb_big2dbl(VALUE x) while (i--) { d = ds[i] + BIGRAD*d; } + if (!RBIGNUM(x)->sign) d = -d; + return d; +} + +double +rb_big2dbl(VALUE x) +{ + double d = big2dbl(x); + if (isinf(d)) { rb_warn("Bignum out of Float range"); d = HUGE_VAL; } - if (!RBIGNUM(x)->sign) d = -d; return d; } @@ -1496,6 +1505,29 @@ rb_big_divmod(VALUE x, VALUE y) return rb_assoc_new(bignorm(div), bignorm(mod)); } +static int +bdigbitsize(BDIGIT x) +{ + int size = 1; + int nb = BITSPERDIG / 2; + BDIGIT bits = (~0 << nb); + + if (!x) return 0; + while (x > 1) { + if (x & bits) { + size += nb; + x >>= nb; + } + x &= ~bits; + nb /= 2; + bits >>= nb; + } + + return size; +} + +static VALUE rb_big_rshift(VALUE,VALUE); + /* * call-seq: * big.quo(numeric) -> float @@ -1511,9 +1543,37 @@ rb_big_divmod(VALUE x, VALUE y) static VALUE rb_big_quo(VALUE x, VALUE y) { - double dx = rb_big2dbl(x); + double dx = big2dbl(x); double dy; + if (isinf(dx)) { +#define DBL_BIGDIG ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG) + VALUE z; + int ex, ey; + + ex = (RBIGNUM(bigtrunc(x))->len - 1) * BITSPERDIG; + ex += bdigbitsize(BDIGITS(x)[RBIGNUM(x)->len - 1]); + ex -= 2 * DBL_BIGDIG * BITSPERDIG; + if (ex) x = rb_big_rshift(x, INT2FIX(ex)); + + switch (TYPE(y)) { + case T_FIXNUM: + y = rb_int2big(FIX2LONG(y)); + case T_BIGNUM: { + ey = (RBIGNUM(bigtrunc(y))->len - 1) * BITSPERDIG; + ey += bdigbitsize(BDIGITS(y)[RBIGNUM(y)->len - 1]); + ey -= DBL_BIGDIG * BITSPERDIG; + if (ey) y = rb_big_rshift(y, INT2FIX(ey)); + bignum: + bigdivrem(x, y, &z, 0); + return rb_float_new(ldexp(big2dbl(z), ex - ey)); + } + case T_FLOAT: + y = dbl2big(ldexp(frexp(RFLOAT(y)->value, &ey), DBL_MANT_DIG)); + ey -= DBL_MANT_DIG; + goto bignum; + } + } switch (TYPE(y)) { case T_FIXNUM: dy = (double)FIX2LONG(y); @@ -1818,8 +1878,6 @@ rb_big_xor(VALUE xx, VALUE yy) return bignorm(z); } -static VALUE rb_big_rshift(VALUE,VALUE); - /* * call-seq: * big << numeric => integer -- cgit v1.2.3