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:
parent
bb60e4615f
commit
9f2378959e
Notes:
git
2022-12-26 12:03:08 +00:00
Merged-By: mrkn <mrkn@ruby-lang.org>
26
numeric.c
26
numeric.c
@ -144,31 +144,37 @@ round_half_down(double x, double s)
|
|||||||
static double
|
static double
|
||||||
round_half_even(double x, double s)
|
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) {
|
if (x > 0.0) {
|
||||||
f = floor(xs);
|
f = floor(vs);
|
||||||
d = xs - f;
|
uf = us + f;
|
||||||
|
d = vs - f;
|
||||||
if (d > 0.5)
|
if (d > 0.5)
|
||||||
d = 1.0;
|
d = 1.0;
|
||||||
else if (d == 0.5 || ((double)((f + 0.5) / s) <= x))
|
else if (d == 0.5 || ((double)((uf + 0.5) / s) <= x))
|
||||||
d = fmod(f, 2.0);
|
d = fmod(uf, 2.0);
|
||||||
else
|
else
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
x = f + d;
|
x = f + d;
|
||||||
}
|
}
|
||||||
else if (x < 0.0) {
|
else if (x < 0.0) {
|
||||||
f = ceil(xs);
|
f = ceil(vs);
|
||||||
d = f - xs;
|
uf = us + f;
|
||||||
|
d = f - vs;
|
||||||
if (d > 0.5)
|
if (d > 0.5)
|
||||||
d = 1.0;
|
d = 1.0;
|
||||||
else if (d == 0.5 || ((double)((f - 0.5) / s) >= x))
|
else if (d == 0.5 || ((double)((uf - 0.5) / s) >= x))
|
||||||
d = fmod(-f, 2.0);
|
d = fmod(-uf, 2.0);
|
||||||
else
|
else
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
x = f - d;
|
x = f - d;
|
||||||
}
|
}
|
||||||
return x;
|
return us + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE fix_lshift(long, unsigned long);
|
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)
|
-4.809999999999999.round(5, half: :even).should eql(-4.81)
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby_bug "", ""..."3.4" do
|
ruby_bug "", ""..."3.3" do
|
||||||
# These numbers are neighbouring floating point numbers round a
|
# These numbers are neighbouring floating point numbers round a
|
||||||
# precise value. They test that the rounding modes work correctly
|
# precise value. They test that the rounding modes work correctly
|
||||||
# round that value and precision is not lost which might cause
|
# 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))
|
assert_equal(-1.26, -1.255.round(2))
|
||||||
end
|
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
|
def test_floor_with_precision
|
||||||
assert_equal(+0.0, +0.001.floor(1))
|
assert_equal(+0.0, +0.001.floor(1))
|
||||||
assert_equal(-0.1, -0.001.floor(1))
|
assert_equal(-0.1, -0.001.floor(1))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user