Bug #12548 ยป round_even.patch
| 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
|
||