[前][次][番号順一覧][スレッド一覧]

ruby-changes:44517

From: nobu <ko1@a...>
Date: Sat, 5 Nov 2016 18:49:45 +0900 (JST)
Subject: [ruby-changes:44517] nobu:r56590 (trunk): numeric.c: round to nearest even

nobu	2016-11-05 18:49:39 +0900 (Sat, 05 Nov 2016)

  New Revision: 56590

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=56590

  Log:
    numeric.c: round to nearest even
    
    * numeric.c (flo_round, int_round): support round-to-nearest-even
      semantics of IEEE 754 to match sprintf behavior, and add `half:`
      optional keyword argument for the old behavior.
      [ruby-core:76273] [Bug #12548]

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/internal.h
    trunk/lib/rexml/functions.rb
    trunk/numeric.c
    trunk/rational.c
    trunk/test/ruby/test_float.rb
    trunk/test/ruby/test_integer.rb
    trunk/test/ruby/test_rational.rb
    trunk/test/test_mathn.rb
Index: NEWS
===================================================================
--- NEWS	(revision 56589)
+++ NEWS	(revision 56590)
@@ -69,6 +69,9 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L69
   * Float#ceil, Float#floor, and Float#truncate now take an optional
     digits, as well as Float#round.  [Feature #12245]
 
+  * Float#round now takes an optional keyword argument, half option, and
+    the default behavior is round-to-nearest-even now.  [Bug #12548]
+
 * Hash
 
   * Hash#transform_values and Hash#transform_values!  [Feature #12512]
@@ -83,6 +86,9 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L86
 
   * Integer#digits for extracting columns of place-value notation [Feature #12447]
 
+  * Int#round now takes an optional keyword argument, half option, and the
+    default behavior is round-to-nearest-even now.  [Bug #12548]
+
 * IO
 
   * IO#gets, IO#readline, IO#each_line, IO#readlines, IO#foreach now takes
@@ -112,6 +118,11 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L118
   * Support CLOCK_MONOTONIC_RAW_APPROX, CLOCK_UPTIME_RAW, and
     CLOCK_UPTIME_RAW_APPROX which are introduced by macOS 10.12.
 
+* Rational
+
+  * Rational#round now takes an optional keyword argument, half option, and
+    the default behavior is round-to-nearest-even now.  [Bug #12548]
+
 * Regexp
 
   * Regexp#match? [Feature #8110]
Index: test/ruby/test_rational.rb
===================================================================
--- test/ruby/test_rational.rb	(revision 56589)
+++ test/ruby/test_rational.rb	(revision 56590)
@@ -597,17 +597,20 @@ class Rational_Test < Test::Unit::TestCa https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rational.rb#L597
   end
 
   def test_trunc
-    [[Rational(13, 5),  [ 2,  3,  2,  3]], #  2.6
-     [Rational(5, 2),   [ 2,  3,  2,  3]], #  2.5
-     [Rational(12, 5),  [ 2,  3,  2,  2]], #  2.4
-     [Rational(-12,5),  [-3, -2, -2, -2]], # -2.4
-     [Rational(-5, 2),  [-3, -2, -2, -3]], # -2.5
-     [Rational(-13, 5), [-3, -2, -2, -3]], # -2.6
+    [[Rational(13, 5),  [ 2,  3,  2,  3,  3,  3]], #  2.6
+     [Rational(5, 2),   [ 2,  3,  2,  2,  2,  3]], #  2.5
+     [Rational(12, 5),  [ 2,  3,  2,  2,  2,  2]], #  2.4
+     [Rational(-12,5),  [-3, -2, -2, -2, -2, -2]], # -2.4
+     [Rational(-5, 2),  [-3, -2, -2, -2, -2, -3]], # -2.5
+     [Rational(-13, 5), [-3, -2, -2, -3, -3, -3]], # -2.6
     ].each do |i, a|
-      assert_equal(a[0], i.floor)
-      assert_equal(a[1], i.ceil)
-      assert_equal(a[2], i.truncate)
-      assert_equal(a[3], i.round)
+      s = proc {i.inspect}
+      assert_equal(a[0], i.floor, s)
+      assert_equal(a[1], i.ceil, s)
+      assert_equal(a[2], i.truncate, s)
+      assert_equal(a[3], i.round, s)
+      assert_equal(a[4], i.round(half: :even), s)
+      assert_equal(a[5], i.round(half: :up), s)
     end
   end
 
Index: test/ruby/test_float.rb
===================================================================
--- test/ruby/test_float.rb	(revision 56589)
+++ test/ruby/test_float.rb	(revision 56590)
@@ -659,6 +659,48 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L659
     }
   end
 
+  def test_round_half_even
+    assert_equal(12.0, 12.5.round(half: :even))
+    assert_equal(14.0, 13.5.round(half: :even))
+
+    assert_equal(2.2, 2.15.round(1, half: :even))
+    assert_equal(2.2, 2.25.round(1, half: :even))
+    assert_equal(2.4, 2.35.round(1, half: :even))
+
+    assert_equal(-2.2, -2.15.round(1, half: :even))
+    assert_equal(-2.2, -2.25.round(1, half: :even))
+    assert_equal(-2.4, -2.35.round(1, half: :even))
+
+    assert_equal(7.1364, 7.13645.round(4, half: :even))
+    assert_equal(7.1365, 7.1364501.round(4, half: :even))
+    assert_equal(7.1364, 7.1364499.round(4, half: :even))
+
+    assert_equal(-7.1364, -7.13645.round(4, half: :even))
+    assert_equal(-7.1365, -7.1364501.round(4, half: :even))
+    assert_equal(-7.1364, -7.1364499.round(4, half: :even))
+  end
+
+  def test_round_half_up
+    assert_equal(13.0, 12.5.round(half: :up))
+    assert_equal(14.0, 13.5.round(half: :up))
+
+    assert_equal(2.2, 2.15.round(1, half: :up))
+    assert_equal(2.3, 2.25.round(1, half: :up))
+    assert_equal(2.4, 2.35.round(1, half: :up))
+
+    assert_equal(-2.2, -2.15.round(1, half: :up))
+    assert_equal(-2.3, -2.25.round(1, half: :up))
+    assert_equal(-2.4, -2.35.round(1, half: :up))
+
+    assert_equal(7.1365, 7.13645.round(4, half: :up))
+    assert_equal(7.1365, 7.1364501.round(4, half: :up))
+    assert_equal(7.1364, 7.1364499.round(4, half: :up))
+
+    assert_equal(-7.1365, -7.13645.round(4, half: :up))
+    assert_equal(-7.1365, -7.1364501.round(4, half: :up))
+    assert_equal(-7.1364, -7.1364499.round(4, half: :up))
+  end
+
   def test_Float
     assert_in_delta(0.125, Float("0.1_2_5"), 0.00001)
     assert_in_delta(0.125, "0.1_2_5__".to_f, 0.00001)
Index: test/ruby/test_integer.rb
===================================================================
--- test/ruby/test_integer.rb	(revision 56589)
+++ test/ruby/test_integer.rb	(revision 56590)
@@ -187,13 +187,49 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L187
     assert_int_equal(11110, 11111.round(-1))
     assert_int_equal(11100, 11111.round(-2))
     assert_int_equal(+200, +249.round(-2))
-    assert_int_equal(+300, +250.round(-2))
+    assert_int_equal(+200, +250.round(-2))
     assert_int_equal(-200, -249.round(-2))
-    assert_int_equal(-300, -250.round(-2))
-    assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71))
-    assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71))
+    assert_int_equal(+200, +249.round(-2, half: :even))
+    assert_int_equal(+200, +250.round(-2, half: :even))
+    assert_int_equal(+300, +349.round(-2, half: :even))
+    assert_int_equal(+400, +350.round(-2, half: :even))
+    assert_int_equal(+200, +249.round(-2, half: :up))
+    assert_int_equal(+300, +250.round(-2, half: :up))
+    assert_int_equal(+300, +349.round(-2, half: :up))
+    assert_int_equal(+400, +350.round(-2, half: :up))
+    assert_int_equal(-200, -250.round(-2))
+    assert_int_equal(-200, -249.round(-2, half: :even))
+    assert_int_equal(-200, -250.round(-2, half: :even))
+    assert_int_equal(-300, -349.round(-2, half: :even))
+    assert_int_equal(-400, -350.round(-2, half: :even))
+    assert_int_equal(-200, -249.round(-2, half: :up))
+    assert_int_equal(-300, -250.round(-2, half: :up))
+    assert_int_equal(-300, -349.round(-2, half: :up))
+    assert_int_equal(-400, -350.round(-2, half: :up))
+    assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71))
     assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71))
     assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71))
+    assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71))
+    assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71))
+    assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71))
+    assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71))
+    assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71, half: :even))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :even))
+    assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :even))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :even))
+    assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71, half: :even))
+    assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71, half: :even))
+    assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :even))
+    assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :even))
+    assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71, half: :up))
+    assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71, half: :up))
+    assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :up))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :up))
+    assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71, half: :up))
+    assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71, half: :up))
+    assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :up))
+    assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :up))
 
     assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1))
     assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1))
Index: test/test_mathn.rb
===================================================================
--- test/test_mathn.rb	(revision 56589)
+++ test/test_mathn.rb	(revision 56590)
@@ -96,17 +96,17 @@ class TestMathn < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_mathn.rb#L96
   def test_round
     assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
       assert_equal( 3, ( 13/5).round)
-      assert_equal( 3, (  5/2).round)
+      assert_equal( 2, (  5/2).round)
       assert_equal( 2, ( 12/5).round)
       assert_equal(-2, (-12/5).round)
-      assert_equal(-3, ( -5/2).round)
+      assert_equal(-2, ( -5/2).round)
       assert_equal(-3, (-13/5).round)
 
       assert_equal( 3, ( 13/5).round(0))
-      assert_equal( 3, (  5/2).round(0))
+      assert_equal( 2, (  5/2).round(0))
       assert_equal( 2, ( 12/5).round(0))
       assert_equal(-2, (-12/5).round(0))
-      assert_equal(-3, ( -5/2).round(0))
+      assert_equal(-2, ( -5/2).round(0))
       assert_equal(-3, (-13/5).round(0))
 
       assert_equal(( 13/5), ( 13/5).round(2))
@@ -115,6 +115,48 @@ class TestMathn < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_mathn.rb#L115
       assert_equal((-12/5), (-12/5).round(2))
       assert_equal(( -5/2), ( -5/2).round(2))
       assert_equal((-13/5), (-13/5).round(2))
+
+      assert_equal( 3, ( 13/5).round(half: :even))
+      assert_equal( 2, (  5/2).round(half: :even))
+      assert_equal( 2, ( 12/5).round(half: :even))
+      assert_equal(-2, (-12/5).round(half: :even))
+      assert_equal(-2, ( -5/2).round(half: :even))
+      assert_equal(-3, (-13/5).round(half: :even))
+
+      assert_equal( 3, ( 13/5).round(0, half: :even))
+      assert_equal( 2, (  5/2).round(0, half: :even))
+      assert_equal( 2, ( 12/5).round(0, half: :even))
+      assert_equal(-2, (-12/5).round(0, half: :even))
+      assert_equal(-2, ( -5/2).round(0, half: :even))
+      assert_equal(-3, (-13/5).round(0, half: :even))
+
+      assert_equal(( 13/5), ( 13/5).round(2, half: :even))
+      assert_equal((  5/2), (  5/2).round(2, half: :even))
+      assert_equal(( 12/5), ( 12/5).round(2, half: :even))
+      assert_equal((-12/5), (-12/5).round(2, half: :even))
+      assert_equal(( -5/2), ( -5/2).round(2, half: :even))
+      assert_equal((-13/5), (-13/5).round(2, half: :even))
+
+      assert_equal( 3, ( 13/5).round(half: :up))
+      assert_equal( 3, (  5/2).round(half: :up))
+      assert_equal( 2, ( 12/5).round(half: :up))
+      assert_equal(-2, (-12/5).round(half: :up))
+      assert_equal(-3, ( -5/2).round(half: :up))
+      assert_equal(-3, (-13/5).round(half: :up))
+
+      assert_equal( 3, ( 13/5).round(0, half: :up))
+      assert_equal( 3, (  5/2).round(0, half: :up))
+      assert_equal( 2, ( 12/5).round(0, half: :up))
+      assert_equal(-2, (-12/5).round(0, half: :up))
+      assert_equal(-3, ( -5/2).round(0, half: :up))
+      assert_equal(-3, (-13/5).round(0, half: :up))
+
+      assert_equal(( 13/5), ( 13/5).round(2, half: :up))
+      assert_equal((  5/2), (  5/2).round(2, half: :up))
+      assert_equal(( 12/5), ( 12/5).round(2, half: :up))
+      assert_equal((-12/5), (-12/5).round(2, half: :up))
+      assert_equal(( -5/2), ( -5/2).round(2, half: :up))
+      assert_equal((-13/5), (-13/5).round(2, half: :up))
     EOS
   end
 end
Index: numeric.c
===================================================================
--- numeric.c	(revision 56589)
+++ numeric.c	(revision 56590)
@@ -93,7 +93,7 @@ round(double x) https://github.com/ruby/ruby/blob/trunk/numeric.c#L93
 #endif
 
 static double
-round_to_nearest(double x, double s)
+round_half_up(double x, double s)
 {
     double f, xs = x * s;
 
@@ -117,12 +117,44 @@ round_to_nearest(double x, double s) https://github.com/ruby/ruby/blob/trunk/numeric.c#L117
     return x;
 }
 
+static double
+round_half_even(double x, double s)
+{
+    double f, d, xs = x * s;
+
+    if (x > 0.0) {
+	f = floor(xs);
+	d = xs - 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
+	    d = 0.0;
+	x = f + d;
+    }
+    else if (x < 0.0) {
+	f = ceil(xs);
+	d = f - xs;
+	if (d > 0.5)
+	    d = 1.0;
+	else if (d == 0.5 || ((double)((f - 0.5) / s) >= x))
+	    d = fmod(-f, 2.0);
+	else
+	    d = 0.0;
+	x = f - d;
+    }
+    return x;
+}
+
 static VALUE fix_uminus(VALUE num);
 static VALUE fix_mul(VALUE x, VALUE y);
 static VALUE fix_lshift(long, unsigned long);
 static VALUE fix_rshift(long, unsigned long);
 static VALUE int_pow(long x, unsigned long y);
 static VALUE int_cmp(VALUE x, VALUE y);
+static VALUE int_odd_p(VALUE x);
+static VALUE int_even_p(VALUE x);
 static int int_round_zero_p(VALUE num, int ndigits);
 VALUE rb_int_floor(VALUE num, int ndigits);
 VALUE rb_int_ceil(VALUE num, int ndigits);
@@ -152,6 +184,38 @@ rb_num_zerodiv(void) https://github.com/ruby/ruby/blob/trunk/numeric.c#L184
     rb_raise(rb_eZeroDivError, "divided by 0");
 }
 
+enum ruby_num_rounding_mode
+rb_num_get_rounding_option(VALUE opts)
+{
+    static ID round_kwds[1];
+    VALUE rounding;
+    const char *s;
+    long l;
+
+    if (!NIL_P(opts)) {
+	if (!round_kwds[0]) {
+	    round_kwds[0] = rb_intern_const("half");
+	}
+	if (!rb_get_kwargs(opts, round_kwds, 0, 1, &rounding)) goto noopt;
+	if (SYMBOL_P(rounding)) rounding = rb_sym2str(rounding);
+	s = StringValueCStr(rounding);
+	l = RSTRING_LEN(rounding);
+	switch (l) {
+	  case 2:
+	    if (strncasecmp(s, "up", 2) == 0)
+		return RUBY_NUM_ROUND_HALF_UP;
+	    break;
+	  case 4:
+	    if (strncasecmp(s, "even", 4) == 0)
+		return RUBY_NUM_ROUND_HALF_EVEN;
+	    break;
+	}
+	rb_raise(rb_eArgError, "unknown rounding mode: %"PRIsVALUE, rounding);
+    }
+  noopt:
+    return RUBY_NUM_ROUND_DEFAULT;
+}
+
 /* experimental API */
 int
 rb_num_to_uint(VALUE val, unsigned int *ret)
@@ -201,7 +265,6 @@ compare_with_zero(VALUE num, ID mid) https://github.com/ruby/ruby/blob/trunk/numeric.c#L265
 #define FIXNUM_NEGATIVE_P(num) ((SIGNED_VALUE)(num) < 0)
 #define FIXNUM_ZERO_P(num) ((num) == INT2FIX(0))
 
-#if 0
 static inline int
 int_pos_p(VALUE num)
 {
@@ -213,7 +276,6 @@ int_pos_p(VALUE num) https://github.com/ruby/ruby/blob/trunk/numeric.c#L276
     }
     return Qnil;
 }
-#endif
 
 static inline int
 int_neg_p(VALUE num)
@@ -1962,11 +2024,27 @@ int_round_zero_p(VALUE num, int ndigits) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2024
     return (-0.415241 * ndigits - 0.125 > bytes);
 }
 
+static SIGNED_VALUE
+int_round_half_even(SIGNED_VALUE x, SIGNED_VALUE y)
+{
+    SIGNED_VALUE z = +(x + y / 2) / y;
+    if ((z * y - x) * 2 == y) {
+	z &= ~1;
+    }
+    return z * y;
+}
+
+static SIGNED_VALUE
+int_round_half_up(SIGNED_VALUE x, SIGNED_VALUE y)
+{
+    return (x + y / 2) / y * y;
+}
+
 /*
  * Assumes num is an Integer, ndigits <= 0
  */
 VALUE
-rb_int_round(VALUE num, int ndigits)
+rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
 {
     VALUE n, f, h, r;
 
@@ -1979,7 +2057,9 @@ rb_int_round(VALUE num, int ndigits) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2057
 	SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
 	int neg = x < 0;
 	if (neg) x = -x;
-	x = (x + y / 2) / y * y;
+	x = ROUND_TO(mode,
+		     int_round_half_up(x, y),
+		     int_round_half_even(x, y));
 	if (neg) x = -x;
 	return LONG2NUM(x);
     }
@@ -1991,7 +2071,11 @@ rb_int_round(VALUE num, int ndigits) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2071
     r = rb_int_modulo(num, f);
     n = rb_int_minus(num, r);
     r = int_cmp(r, h);
-    if (FIXNUM_POSITIVE_P(r) || (FIXNUM_ZERO_P(r) && !int_neg_p(num))) {
+    if (FIXNUM_POSITIVE_P(r) ||
+	(FIXNUM_ZERO_P(r) &&
+	 ROUND_TO(mode,
+		  int_pos_p(num),
+		  int_odd_p(rb_int_idiv(n, f))))) {
 	n = rb_int_plus(n, f);
     }
     return n;
@@ -2109,21 +2193,27 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/numeric.c#L2193
 flo_round(int argc, VALUE *argv, VALUE num)
 {
     double number, f, x;
+    VALUE nd, opt;
     int ndigits = 0;
+    enum ruby_num_rounding_mode mode;
 
-    if (rb_check_arity(argc, 0, 1)) {
-	ndigits = NUM2INT(argv[0]);
+    if (rb_scan_args(argc, argv, "01:", &nd, &opt)) {
+	ndigits = NUM2INT(nd);
     }
+    mode = rb_num_get_rounding_option(opt);
     if (ndigits < 0) {
-	return rb_int_round(flo_to_i(num), ndigits);
+	return rb_int_round(flo_to_i(num), ndigits, mode);
     }
     number  = RFLOAT_VALUE(num);
     if (ndigits == 0) {
-	return dbl2ival(round(number));
+	x = ROUND_TO(mode,
+		     round(number), round_half_even(number, 1.0));
+	return dbl2ival(x);
     }
     if (float_invariant_round(number, ndigits, &num)) return num;
     f = pow(10, ndigits);
-    x = round_to_nearest(number, f);
+    x = ROUND_TO(mode,
+		 round_half_up(number, f), round_half_even(number, f));
     return DBL2NUM(x / f);
 }
 
@@ -4862,16 +4952,19 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/numeric.c#L4952
 int_round(int argc, VALUE* argv, VALUE num)
 {
     int ndigits;
+    int mode;
+    VALUE nd, opt;
 
-    if (!rb_check_arity(argc, 0, 1)) return num;
-    ndigits = NUM2INT(argv[0]);
+    if (!rb_scan_args(argc, argv, "01:", &nd, &opt)) return num;
+    ndigits = NUM2INT(nd);
+    mode = rb_num_get_rounding_option(opt);
     if (ndigits > 0) {
 	return rb_Float(num);
     }
     if (ndigits == 0) {
 	return num;
     }
-    return rb_int_round(num, ndigits);
+    return rb_int_round(num, ndigits, mode);
 }
 
 /*
Index: lib/rexml/functions.rb
===================================================================
--- lib/rexml/functions.rb	(revision 56589)
+++ lib/rexml/functions.rb	(revision 56590)
@@ -205,8 +205,8 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/functions.rb#L205
 
       # Now, get the bounds.  The XPath bounds are 1..length; the ruby bounds
       # are 0..length.  Therefore, we have to offset the bounds by one.
-      ruby_start = ruby_start.round - 1
-      ruby_length = ruby_length.round
+      ruby_start = round(ruby_start) - 1
+      ruby_length = round(ruby_length)
 
       if ruby_start < 0
        ruby_length += ruby_start unless infinite_length
@@ -376,10 +376,13 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/functions.rb#L376
     end
 
     def Functions::round( number )
+      number = number(number)
       begin
-        number(number).round
+        neg = number.negative?
+        number = number.abs.round(half: :up)
+        neg ? -number : number
       rescue FloatDomainError
-        number(number)
+        number
       end
     end
 
Index: internal.h
===================================================================
--- internal.h	(revision 56589)
+++ internal.h	(revision 56590)
@@ -1135,6 +1135,17 @@ VALUE rb_math_sqrt(VALUE); https://github.com/ruby/ruby/blob/trunk/internal.h#L1135
 void Init_newline(void);
 
 /* numeric.c */
+#ifndef ROUND_DEFAULT
+# define ROUND_DEFAULT RUBY_NUM_ROUND_HALF_EVEN
+#endif
+enum ruby_num_rounding_mode {
+    RUBY_NUM_ROUND_HALF_UP,
+    RUBY_NUM_ROUND_HALF_EVEN,
+    RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT
+};
+#define ROUND_TO(mode, up, even) \
+    ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : up)
+
 int rb_num_to_uint(VALUE val, unsigned int *ret);
 VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE st (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]