Project

General

Profile

Bug #12548 ยป round_even.patch

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

View differences:

ChangeLog
1
Wed Sep 22 4:51:00 2016  Noah Gibbs <the.codefolio.guy@gmail.com>
2

  
3
	* numeric.c (round_to_nearest): support IEEE 754 round-to-nearest-even
4
	  semantics to match Ruby's sprintf behavior.
5
	* test/ruby/test_float.c: add test for round-to-nearest-even behavior.
6

  
1 7
Wed Sep 21 17:43:53 2016  NARUSE, Yui  <naruse@ruby-lang.org>
2 8

  
3 9
	* process.c (InitVM_process): Support CLOCK_MONOTONIC_RAW_APPROX,
numeric.c
92 92
}
93 93
#endif
94 94

  
95
/*
96
 * Round x*s to nearest multiple of s, breaking ties toward the
97
 * nearest even number. This allows IEEE 754 rounding to nearest even:
98
 * https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
99
 */
100

  
95 101
static double
96 102
round_to_nearest(double x, double s)
97 103
{
98 104
    double f, xs = x * s;
99 105

  
100
#ifdef HAVE_ROUND
101
    f = round(xs);
102
#endif
106
    int f_down = floor(xs);
107
    int even_upward = (f_down % 2);
108

  
103 109
    if (x > 0) {
104
#ifndef HAVE_ROUND
105
	f = floor(xs);
106
#endif
107
	if ((double)((f + 0.5) / s) <= x) f += 1;
108
	x = f;
110
        f = floor(xs);
111
	if (UNLIKELY(((f + 0.5) / s) == x) && even_upward) f += 1;
112
	else if ((double)((f + 0.5) / s) < x) f += 1;
109 113
    }
110 114
    else {
111
#ifndef HAVE_ROUND
112
	f = ceil(xs);
113
#endif
114
	if ((double)((f - 0.5) / s) >= x) f -= 1;
115
	x = f;
115
        f = ceil(xs);
116
	if (UNLIKELY(((f - 0.5) / s) == x) && !even_upward) f -= 1;
117
	else if ((double)((f - 0.5) / s) > x) f -= 1;
116 118
    }
117
    return x;
119
    return f;
118 120
}
119 121

  
120 122
static VALUE fix_uminus(VALUE num);
......
2110 2112
    }
2111 2113
    number  = RFLOAT_VALUE(num);
2112 2114
    if (ndigits == 0) {
2115
	long fl = floor(number);
2116
	long ce = ceil(number);
2117
	if(fl != ce && UNLIKELY(number == ((double)fl + ce) / 2)) {
2118
	    return dbl2ival((fl % 2 == 0) ? fl : ce);
2119
	}
2113 2120
	return dbl2ival(round(number));
2114 2121
    }
2115 2122
    if (float_invariant_round(number, ndigits, &num)) return num;
......
2118 2125
    return DBL2NUM(x / f);
2119 2126
}
2120 2127

  
2128
/*
2129
 * Returns true if the float is its own exact representation to the specified
2130
 * number of digits.
2131
 */
2132

  
2121 2133
static int
2122 2134
float_invariant_round(double number, int ndigits, VALUE *num)
2123 2135
{
test/matrix/test_vector.rb
159 159
  end
160 160

  
161 161
  def test_round
162
    assert_equal(Vector[1.234, 2.345, 3.40].round(2), Vector[1.23, 2.35, 3.4])
162
    assert_equal(Vector[1.234, 2.355, 3.40].round(2), Vector[1.23, 2.36, 3.4])
163 163
  end
164 164

  
165 165
  def test_covector
test/rexml/test_functions.rb
164 164
      }
165 165
      good.each do |key, value|
166 166
        (0..3).each do |i|
167
          xpath = "//b[number(@id) = #{key}(#{i+0.5})]"
167
          xpath = "//b[number(@id) = #{key}(#{i+0.50001})]"
168 168
          assert_equal(value[i], REXML::XPath.match(doc, xpath))
169 169
        end
170 170
      end
test/ruby/test_float.rb
44 44
    nan_test(nan, -1.0/0);
45 45
  end
46 46

  
47
  def test_round_even
48
    assert_equal(12.0, 12.5.round);
49
    assert_equal(14.0, 13.5.round);
50

  
51
    assert_equal(2.2, 2.15.round(1));
52
    assert_equal(2.2, 2.25.round(1));
53
    assert_equal(2.4, 2.35.round(1));
54

  
55
    assert_equal(-2.2, -2.15.round(1));
56
    assert_equal(-2.2, -2.25.round(1));
57
    assert_equal(-2.4, -2.35.round(1));
58

  
59
    assert_equal(7.1364, 7.13645.round(4));
60
    assert_equal(7.1365, 7.1364501.round(4));
61
    assert_equal(7.1364, 7.1364499.round(4));
62

  
63
    assert_equal(-7.1364, -7.13645.round(4));
64
    assert_equal(-7.1365, -7.1364501.round(4));
65
    assert_equal(-7.1364, -7.1364499.round(4));
66
end
67

  
47 68
  def test_precision
48 69
    u = 3.7517675036461267e+17
49 70
    v = sprintf("%.16e", u).to_f