summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numeric.c26
-rw-r--r--spec/ruby/core/float/round_spec.rb2
-rw-r--r--test/ruby/test_float.rb11
3 files changed, 28 insertions, 11 deletions
diff --git a/numeric.c b/numeric.c
index b191b25d79..0312f4939f 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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);
diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb
index 50106025f9..e5a8f534e7 100644
--- a/spec/ruby/core/float/round_spec.rb
+++ b/spec/ruby/core/float/round_spec.rb
@@ -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
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index fdc5d28ed7..35cde1e951 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -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))