diff --git a/numeric.c b/numeric.c index 345067c6a5..5564a6eceb 100644 --- a/numeric.c +++ b/numeric.c @@ -1829,20 +1829,24 @@ flo_prev_float(VALUE vx) VALUE rb_float_floor(VALUE num, int ndigits) { - double number, f; + double number; number = RFLOAT_VALUE(num); if (number == 0.0) { return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0); } if (ndigits > 0) { int binexp; + double f, mul, res; frexp(number, &binexp); if (float_round_overflow(ndigits, binexp)) return num; if (number > 0.0 && float_round_underflow(ndigits, binexp)) return DBL2NUM(0.0); f = pow(10, ndigits); - f = floor(number * f) / f; - return DBL2NUM(f); + mul = floor(number * f); + res = (mul + 1) / f; + if (res > number) + res = mul / f; + return DBL2NUM(res); } else { num = dbl2ival(floor(number)); diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb index 066afc83a2..b5486d387c 100644 --- a/test/ruby/test_numeric.rb +++ b/test/ruby/test_numeric.rb @@ -229,6 +229,15 @@ class TestNumeric < Test::Unit::TestCase assert_equal(-1, a.truncate) end + def test_floor_ceil_ndigits + bug17183 = "[ruby-core:100090]" + f = 291.4 + 31.times do |i| + assert_equal(291.4, f.floor(i+1), bug17183) + assert_equal(291.4, f.ceil(i+1), bug17183) + end + end + def assert_step(expected, (from, *args), inf: false) kw = args.last.is_a?(Hash) ? args.pop : {} enum = from.step(*args, **kw)