diff options
-rw-r--r-- | internal/rational.h | 1 | ||||
-rw-r--r-- | numeric.c | 4 | ||||
-rw-r--r-- | rational.c | 6 | ||||
-rw-r--r-- | test/ruby/test_numeric.rb | 8 |
4 files changed, 19 insertions, 0 deletions
diff --git a/internal/rational.h b/internal/rational.h index 6bbd2a9810..a9e96742c9 100644 --- a/internal/rational.h +++ b/internal/rational.h @@ -39,6 +39,7 @@ VALUE rb_rational_cmp(VALUE self, VALUE other); VALUE rb_rational_pow(VALUE self, VALUE other); VALUE rb_rational_floor(VALUE self, int ndigits); VALUE rb_numeric_quo(VALUE x, VALUE y); +VALUE rb_flo_round_by_rational(int argc, VALUE *argv, VALUE num); VALUE rb_float_numerator(VALUE x); VALUE rb_float_denominator(VALUE x); @@ -2230,6 +2230,10 @@ flo_round(int argc, VALUE *argv, VALUE num) frexp(number, &binexp); if (float_round_overflow(ndigits, binexp)) return num; if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0); + if (ndigits > 14) { + /* In this case, pow(10, ndigits) may not be accurate. */ + return rb_flo_round_by_rational(argc, argv, num); + } f = pow(10, ndigits); x = ROUND_CALL(mode, round, (number, f)); return DBL2NUM(x / f); diff --git a/rational.c b/rational.c index 76a4264e0a..7324f78621 100644 --- a/rational.c +++ b/rational.c @@ -1540,6 +1540,12 @@ nurat_round_n(int argc, VALUE *argv, VALUE self) return f_round_common(argc, argv, self, round_func); } +VALUE +rb_flo_round_by_rational(int argc, VALUE *argv, VALUE num) +{ + return nurat_to_f(nurat_round_n(argc, argv, float_to_r(num))); +} + static double nurat_to_double(VALUE self) { diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb index b5486d387c..0593cb535d 100644 --- a/test/ruby/test_numeric.rb +++ b/test/ruby/test_numeric.rb @@ -200,6 +200,14 @@ class TestNumeric < Test::Unit::TestCase assert_nil(a <=> :foo) end + def test_float_round_ndigits + bug14635 = "[ruby-core:86323]" + f = 0.5 + 31.times do |i| + assert_equal(0.5, f.round(i+1), bug14635 + " (argument: #{i+1})") + end + end + def test_floor_ceil_round_truncate a = Class.new(Numeric) do def to_f; 1.5; end |