diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-05-02 08:12:31 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-05-02 08:12:31 +0000 |
commit | 8269ab2e1fd073f2970535ccd796213db29cd2bd (patch) | |
tree | 06692d35cf9219e05c30773c23aecb68f00e4f13 | |
parent | 1db71b2ecae2d35c700de5a4d8257c3617a093ae (diff) | |
download | ruby-8269ab2e1fd073f2970535ccd796213db29cd2bd.tar.gz |
* 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
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | bignum.c | 70 |
2 files changed, 68 insertions, 6 deletions
@@ -1,3 +1,7 @@ +Wed May 2 17:13:26 2007 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * bignum.c (rb_big_quo): now calculate in integer. [ruby-dev:30753] + Wed May 2 15:14:56 2007 Koichi Sasada <ko1@atdot.net> * eval_method.h: add redefine checks ([ruby-dev:30751]). @@ -13,6 +13,7 @@ #include "ruby.h" #include <math.h> +#include <float.h> #include <ctype.h> #ifdef HAVE_IEEEFP_H #include <ieeefp.h> @@ -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 |