mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Make Float#floor with ndigits argument handle error
The previous implementation could result in a returned float that is 1/(10**ndigits) too low. First try adding one before dividing, and if that results in a value that is greater than the initial number, then try the original calculation. Spec added for ciel, but the issue doesn't appear to affect ciel, at least not for the same number. If the issue does effect ciel, a similar fix could probably work for it. Fixes [Bug #18018]
This commit is contained in:
parent
338b604b32
commit
35e467080c
Notes:
git
2021-07-28 03:01:05 +09:00
2 changed files with 16 additions and 3 deletions
10
numeric.c
10
numeric.c
|
@ -1829,20 +1829,24 @@ flo_prev_float(VALUE vx)
|
||||||
VALUE
|
VALUE
|
||||||
rb_float_floor(VALUE num, int ndigits)
|
rb_float_floor(VALUE num, int ndigits)
|
||||||
{
|
{
|
||||||
double number, f;
|
double number;
|
||||||
number = RFLOAT_VALUE(num);
|
number = RFLOAT_VALUE(num);
|
||||||
if (number == 0.0) {
|
if (number == 0.0) {
|
||||||
return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
|
return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
|
||||||
}
|
}
|
||||||
if (ndigits > 0) {
|
if (ndigits > 0) {
|
||||||
int binexp;
|
int binexp;
|
||||||
|
double f, mul, res;
|
||||||
frexp(number, &binexp);
|
frexp(number, &binexp);
|
||||||
if (float_round_overflow(ndigits, binexp)) return num;
|
if (float_round_overflow(ndigits, binexp)) return num;
|
||||||
if (number > 0.0 && float_round_underflow(ndigits, binexp))
|
if (number > 0.0 && float_round_underflow(ndigits, binexp))
|
||||||
return DBL2NUM(0.0);
|
return DBL2NUM(0.0);
|
||||||
f = pow(10, ndigits);
|
f = pow(10, ndigits);
|
||||||
f = floor(number * f) / f;
|
mul = floor(number * f);
|
||||||
return DBL2NUM(f);
|
res = (mul + 1) / f;
|
||||||
|
if (res > number)
|
||||||
|
res = mul / f;
|
||||||
|
return DBL2NUM(res);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
num = dbl2ival(floor(number));
|
num = dbl2ival(floor(number));
|
||||||
|
|
|
@ -229,6 +229,15 @@ class TestNumeric < Test::Unit::TestCase
|
||||||
assert_equal(-1, a.truncate)
|
assert_equal(-1, a.truncate)
|
||||||
end
|
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)
|
def assert_step(expected, (from, *args), inf: false)
|
||||||
kw = args.last.is_a?(Hash) ? args.pop : {}
|
kw = args.last.is_a?(Hash) ? args.pop : {}
|
||||||
enum = from.step(*args, **kw)
|
enum = from.step(*args, **kw)
|
||||||
|
|
Loading…
Reference in a new issue