summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/rational.h1
-rw-r--r--numeric.c4
-rw-r--r--rational.c6
-rw-r--r--test/ruby/test_numeric.rb8
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);
diff --git a/numeric.c b/numeric.c
index 5f7c16218a..a1801f9654 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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