diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | bignum.c | 103 | ||||
-rw-r--r-- | numeric.c | 4 | ||||
-rw-r--r-- | rational.c | 2 | ||||
-rw-r--r-- | test/ruby/test_bignum.rb | 18 |
5 files changed, 91 insertions, 47 deletions
@@ -1,3 +1,14 @@ +Wed Jun 17 21:25:54 2009 Tadayoshi Funaba <[email protected]> + + * bignum.c (rb_big_fdiv): checks whether the given second argument + can be converted to float properly. + + * numeric.c (fix_fdiv): calls rb_big_fdiv when the given second + argument is a bignum. + + * rational.c (nurat_fdiv): should calculate Float(x/y), not + Float(x)/Float(y). + Wed Jun 17 16:57:40 2009 Yukihiro Matsumoto <[email protected]> * load.c (rb_f_require): RDoc updated. a patch from Run Paint Run @@ -2385,6 +2385,52 @@ big_shift(VALUE x, long n) return x; } +static VALUE +big_fdiv(VALUE x, VALUE y) +{ +#define DBL_BIGDIG ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG) + VALUE z; + long l, ex, ey; + int i; + + bigtrunc(x); + l = RBIGNUM_LEN(x) - 1; + ex = l * BITSPERDIG; + ex += bdigbitsize(BDIGITS(x)[l]); + ex -= 2 * DBL_BIGDIG * BITSPERDIG; + if (ex) x = big_shift(x, ex); + + switch (TYPE(y)) { + case T_FIXNUM: + y = rb_int2big(FIX2LONG(y)); + case T_BIGNUM: { + bigtrunc(y); + l = RBIGNUM_LEN(y) - 1; + ey = l * BITSPERDIG; + ey += bdigbitsize(BDIGITS(y)[l]); + ey -= DBL_BIGDIG * BITSPERDIG; + if (ey) y = big_shift(y, ey); + bignum: + bigdivrem(x, y, &z, 0); + l = ex - ey; +#if SIZEOF_LONG > SIZEOF_INT + { + /* Visual C++ can't be here */ + if (l > INT_MAX) return DBL2NUM(ruby_div0(1.0)); + if (l < INT_MIN) return DBL2NUM(0.0); + } +#endif + return DBL2NUM(ldexp(big2dbl(z), (int)l)); + } + case T_FLOAT: + y = dbl2big(ldexp(frexp(RFLOAT_VALUE(y), &i), DBL_MANT_DIG)); + ey = i - DBL_MANT_DIG; + goto bignum; + } + rb_bug("big_fdiv"); + /* NOTREACHED */ +} + /* * call-seq: * big.fdiv(numeric) -> float @@ -2397,65 +2443,32 @@ big_shift(VALUE x, long n) * */ -static VALUE + +VALUE rb_big_fdiv(VALUE x, VALUE y) { - double dx = big2dbl(x); - double dy; + double dx, dy; - if (isinf(dx)) { -#define DBL_BIGDIG ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG) - VALUE z; - long l, ex, ey; - int i; - - bigtrunc(x); - l = RBIGNUM_LEN(x) - 1; - ex = l * BITSPERDIG; - ex += bdigbitsize(BDIGITS(x)[l]); - ex -= 2 * DBL_BIGDIG * BITSPERDIG; - if (ex) x = big_shift(x, ex); - - switch (TYPE(y)) { - case T_FIXNUM: - y = rb_int2big(FIX2LONG(y)); - case T_BIGNUM: { - bigtrunc(y); - l = RBIGNUM_LEN(y) - 1; - ey = l * BITSPERDIG; - ey += bdigbitsize(BDIGITS(y)[l]); - ey -= DBL_BIGDIG * BITSPERDIG; - if (ey) y = big_shift(y, ey); - bignum: - bigdivrem(x, y, &z, 0); - l = ex - ey; -#if SIZEOF_LONG > SIZEOF_INT - { - /* Visual C++ can't be here */ - if (l > INT_MAX) return DBL2NUM(ruby_div0(1.0)); - if (l < INT_MIN) return DBL2NUM(0.0); - } -#endif - return DBL2NUM(ldexp(big2dbl(z), (int)l)); - } - case T_FLOAT: - if (isnan(RFLOAT_VALUE(y))) return y; - y = dbl2big(ldexp(frexp(RFLOAT_VALUE(y), &i), DBL_MANT_DIG)); - ey = i - DBL_MANT_DIG; - goto bignum; - } - } + dx = big2dbl(x); switch (TYPE(y)) { case T_FIXNUM: dy = (double)FIX2LONG(y); + if (isinf(dx)) + return big_fdiv(x, y); break; case T_BIGNUM: dy = rb_big2dbl(y); + if (isinf(dx) || isinf(dy)) + return big_fdiv(x, y); break; case T_FLOAT: dy = RFLOAT_VALUE(y); + if (isnan(dy)) + return y; + if (isinf(dx)) + return big_fdiv(x, y); break; default: @@ -2269,6 +2269,8 @@ fixdivmod(long x, long y, long *divp, long *modp) * */ +VALUE rb_big_fdiv(VALUE x, VALUE y); + static VALUE fix_fdiv(VALUE x, VALUE y) { @@ -2277,7 +2279,7 @@ fix_fdiv(VALUE x, VALUE y) } switch (TYPE(y)) { case T_BIGNUM: - return DBL2NUM((double)FIX2LONG(x) / rb_big2dbl(y)); + return rb_big_fdiv(rb_int2big(FIX2LONG(x)), y); case T_FLOAT: return DBL2NUM((double)FIX2LONG(x) / RFLOAT_VALUE(y)); default: diff --git a/rational.c b/rational.c index 3fb0b2a043..de676dc473 100644 --- a/rational.c +++ b/rational.c @@ -769,7 +769,7 @@ nurat_div(VALUE self, VALUE other) static VALUE nurat_fdiv(VALUE self, VALUE other) { - return f_div(f_to_f(self), other); + return f_to_f(f_div(self, other)); } static VALUE diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index 29fda85572..b77fd8f683 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -4,6 +4,9 @@ class TestBignum < Test::Unit::TestCase def setup @verbose = $VERBOSE $VERBOSE = nil + @fmax = Float::MAX.to_i + @fmax2 = @fmax * 2 + @big = (1 << 63) - 1 end def teardown @@ -395,4 +398,19 @@ class TestBignum < Test::Unit::TestCase e = assert_raise(RangeError) {(1 << big).to_s} assert_match(/too big to convert/, e.message) end + + def test_fix_fdiv + assert_not_equal(0, 1.fdiv(@fmax2)) + assert_in_delta(0.5, 1.fdiv(@fmax2) * @fmax, 0.01) + end + + def test_big_fdiv + assert_equal(1, @big.fdiv(@big)) + assert_not_equal(0, @big.fdiv(@fmax2)) + assert_not_equal(0, @fmax2.fdiv(@big)) + assert_not_equal(0, @fmax2.fdiv(@fmax2)) + assert_in_delta(0.5, @fmax.fdiv(@fmax2), 0.01) + assert_in_delta(1.0, @fmax2.fdiv(@fmax2), 0.01) + end + end |