Project

General

Profile

Bug #12548 ยป round_even.patch

noahgibbs (Noah Gibbs), 09/22/2016 04:40 AM

View differences:

ChangeLog
Wed Sep 22 4:51:00 2016 Noah Gibbs <the.codefolio.guy@gmail.com>
* numeric.c (round_to_nearest): support IEEE 754 round-to-nearest-even
semantics to match Ruby's sprintf behavior.
* test/ruby/test_float.c: add test for round-to-nearest-even behavior.
Wed Sep 21 17:43:53 2016 NARUSE, Yui <naruse@ruby-lang.org>
* process.c (InitVM_process): Support CLOCK_MONOTONIC_RAW_APPROX,
numeric.c
}
#endif
/*
* Round x*s to nearest multiple of s, breaking ties toward the
* nearest even number. This allows IEEE 754 rounding to nearest even:
* https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
*/
static double
round_to_nearest(double x, double s)
{
double f, xs = x * s;
#ifdef HAVE_ROUND
f = round(xs);
#endif
int f_down = floor(xs);
int even_upward = (f_down % 2);
if (x > 0) {
#ifndef HAVE_ROUND
f = floor(xs);
#endif
if ((double)((f + 0.5) / s) <= x) f += 1;
x = f;
f = floor(xs);
if (UNLIKELY(((f + 0.5) / s) == x) && even_upward) f += 1;
else if ((double)((f + 0.5) / s) < x) f += 1;
}
else {
#ifndef HAVE_ROUND
f = ceil(xs);
#endif
if ((double)((f - 0.5) / s) >= x) f -= 1;
x = f;
f = ceil(xs);
if (UNLIKELY(((f - 0.5) / s) == x) && !even_upward) f -= 1;
else if ((double)((f - 0.5) / s) > x) f -= 1;
}
return x;
return f;
}
static VALUE fix_uminus(VALUE num);
......
}
number = RFLOAT_VALUE(num);
if (ndigits == 0) {
long fl = floor(number);
long ce = ceil(number);
if(fl != ce && UNLIKELY(number == ((double)fl + ce) / 2)) {
return dbl2ival((fl % 2 == 0) ? fl : ce);
}
return dbl2ival(round(number));
}
if (float_invariant_round(number, ndigits, &num)) return num;
......
return DBL2NUM(x / f);
}
/*
* Returns true if the float is its own exact representation to the specified
* number of digits.
*/
static int
float_invariant_round(double number, int ndigits, VALUE *num)
{
test/matrix/test_vector.rb
end
def test_round
assert_equal(Vector[1.234, 2.345, 3.40].round(2), Vector[1.23, 2.35, 3.4])
assert_equal(Vector[1.234, 2.355, 3.40].round(2), Vector[1.23, 2.36, 3.4])
end
def test_covector
test/rexml/test_functions.rb
}
good.each do |key, value|
(0..3).each do |i|
xpath = "//b[number(@id) = #{key}(#{i+0.5})]"
xpath = "//b[number(@id) = #{key}(#{i+0.50001})]"
assert_equal(value[i], REXML::XPath.match(doc, xpath))
end
end
test/ruby/test_float.rb
nan_test(nan, -1.0/0);
end
def test_round_even
assert_equal(12.0, 12.5.round);
assert_equal(14.0, 13.5.round);
assert_equal(2.2, 2.15.round(1));
assert_equal(2.2, 2.25.round(1));
assert_equal(2.4, 2.35.round(1));
assert_equal(-2.2, -2.15.round(1));
assert_equal(-2.2, -2.25.round(1));
assert_equal(-2.4, -2.35.round(1));
assert_equal(7.1364, 7.13645.round(4));
assert_equal(7.1365, 7.1364501.round(4));
assert_equal(7.1364, 7.1364499.round(4));
assert_equal(-7.1364, -7.13645.round(4));
assert_equal(-7.1365, -7.1364501.round(4));
assert_equal(-7.1364, -7.1364499.round(4));
end
def test_precision
u = 3.7517675036461267e+17
v = sprintf("%.16e", u).to_f
    (1-1/1)