mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Use Rational for Float#round with ndigits > 14
ndigits higher than 14 can result in values that are slightly too large due to floating point limitations. Converting to rational for the calculation and then back to float fixes these issues. Fixes [Bug #14635] Fixes [Bug #17183] Co-authored by: Yusuke Endoh <mame@ruby-lang.org>
This commit is contained in:
parent
0d3520b063
commit
d16b68cb22
Notes:
git
2021-08-07 07:04:11 +09:00
4 changed files with 19 additions and 0 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue