aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--bignum.c103
-rw-r--r--numeric.c4
-rw-r--r--rational.c2
-rw-r--r--test/ruby/test_bignum.rb18
5 files changed, 91 insertions, 47 deletions
diff --git a/ChangeLog b/ChangeLog
index b7a3fd613f..12c72352cf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Wed Jun 17 21:25:54 2009 Tadayoshi Funaba <tadf@dotrb.org>
+
+ * 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 <matz@ruby-lang.org>
* load.c (rb_f_require): RDoc updated. a patch from Run Paint Run
diff --git a/bignum.c b/bignum.c
index 2f8ab20e32..cba4210946 100644
--- a/bignum.c
+++ b/bignum.c
@@ -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:
diff --git a/numeric.c b/numeric.c
index 10bc424738..a4ccd01b5b 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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