numeric.c: Fix round_half_even for specific values (#7023)

Handle the integert and the float parts separately in round_half_even
to prevent error occursions in floating point calculation.
This commit is contained in:
Kenta Murata 2022-12-26 21:02:47 +09:00 коммит произвёл GitHub
Родитель bb60e4615f
Коммит 9f2378959e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 28 добавлений и 11 удалений

Просмотреть файл

@ -144,31 +144,37 @@ round_half_down(double x, double s)
static double
round_half_even(double x, double s)
{
double f, d, xs = x * s;
double u, v, us, vs, f, d, uf;
v = modf(x, &u);
us = u * s;
vs = v * s;
if (x > 0.0) {
f = floor(xs);
d = xs - f;
f = floor(vs);
uf = us + f;
d = vs - f;
if (d > 0.5)
d = 1.0;
else if (d == 0.5 || ((double)((f + 0.5) / s) <= x))
d = fmod(f, 2.0);
else if (d == 0.5 || ((double)((uf + 0.5) / s) <= x))
d = fmod(uf, 2.0);
else
d = 0.0;
x = f + d;
}
else if (x < 0.0) {
f = ceil(xs);
d = f - xs;
f = ceil(vs);
uf = us + f;
d = f - vs;
if (d > 0.5)
d = 1.0;
else if (d == 0.5 || ((double)((f - 0.5) / s) >= x))
d = fmod(-f, 2.0);
else if (d == 0.5 || ((double)((uf - 0.5) / s) >= x))
d = fmod(-uf, 2.0);
else
d = 0.0;
x = f - d;
}
return x;
return us + x;
}
static VALUE fix_lshift(long, unsigned long);

Просмотреть файл

@ -136,7 +136,7 @@ describe "Float#round" do
-4.809999999999999.round(5, half: :even).should eql(-4.81)
end
ruby_bug "", ""..."3.4" do
ruby_bug "", ""..."3.3" do
# These numbers are neighbouring floating point numbers round a
# precise value. They test that the rounding modes work correctly
# round that value and precision is not lost which might cause

Просмотреть файл

@ -486,6 +486,17 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-1.26, -1.255.round(2))
end
def test_round_half_even_with_precision
assert_equal(767573.18759, 767573.1875850001.round(5, half: :even))
assert_equal(767573.18758, 767573.187585.round(5, half: :even))
assert_equal(767573.18758, 767573.1875849998.round(5, half: :even))
assert_equal(767573.18758, 767573.187575.round(5, half: :even))
assert_equal(-767573.18759, -767573.1875850001.round(5, half: :even))
assert_equal(-767573.18758, -767573.187585.round(5, half: :even))
assert_equal(-767573.18758, -767573.1875849998.round(5, half: :even))
assert_equal(-767573.18758, -767573.187575.round(5, half: :even))
end
def test_floor_with_precision
assert_equal(+0.0, +0.001.floor(1))
assert_equal(-0.1, -0.001.floor(1))