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

ruby-changes:44824

From: nobu <ko1@a...>
Date: Fri, 25 Nov 2016 15:28:05 +0900 (JST)
Subject: [ruby-changes:44824] nobu:r56897 (trunk): round-down

nobu	2016-11-25 15:28:00 +0900 (Fri, 25 Nov 2016)

  New Revision: 56897

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

  Log:
    round-down
    
    * numeric.c (round_half_down, int_round_half_down): support
      round-down mode.

  Modified files:
    trunk/internal.h
    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: internal.h
===================================================================
--- internal.h	(revision 56896)
+++ internal.h	(revision 56897)
@@ -1149,16 +1149,17 @@ void Init_newline(void); https://github.com/ruby/ruby/blob/trunk/internal.h#L1149
 enum ruby_num_rounding_mode {
     RUBY_NUM_ROUND_HALF_UP,
     RUBY_NUM_ROUND_HALF_EVEN,
+    RUBY_NUM_ROUND_HALF_DOWN,
     RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT
 };
-#define ROUND_TO(mode, even, up) \
+#define ROUND_TO(mode, even, up, down) \
     ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
-     up)
+     (mode) == RUBY_NUM_ROUND_HALF_UP ? up : down)
 #define ROUND_FUNC(mode, name) \
-    ROUND_TO(mode, name##_half_even, name##_half_up)
+    ROUND_TO(mode, name##_half_even, name##_half_up, name##_half_down)
 #define ROUND_CALL(mode, name, args) \
     ROUND_TO(mode, name##_half_even args, \
-	     name##_half_up args)
+	     name##_half_up args, name##_half_down args)
 
 int rb_num_to_uint(VALUE val, unsigned int *ret);
 VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
Index: test/ruby/test_rational.rb
===================================================================
--- test/ruby/test_rational.rb	(revision 56896)
+++ test/ruby/test_rational.rb	(revision 56897)
@@ -597,12 +597,12 @@ 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,  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
+    [[Rational(13, 5),  [ 2,  3,  2,  3,  3,  3,  3]], #  2.6
+     [Rational(5, 2),   [ 2,  3,  2,  2,  2,  3,  2]], #  2.5
+     [Rational(12, 5),  [ 2,  3,  2,  2,  2,  2,  2]], #  2.4
+     [Rational(-12,5),  [-3, -2, -2, -2, -2, -2, -2]], # -2.4
+     [Rational(-5, 2),  [-3, -2, -2, -2, -2, -3, -2]], # -2.5
+     [Rational(-13, 5), [-3, -2, -2, -3, -3, -3, -3]], # -2.6
     ].each do |i, a|
       s = proc {i.inspect}
       assert_equal(a[0], i.floor, s)
@@ -611,6 +611,7 @@ class Rational_Test < Test::Unit::TestCa https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rational.rb#L611
       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)
+      assert_equal(a[6], i.round(half: :down), s)
     end
   end
 
Index: test/ruby/test_integer.rb
===================================================================
--- test/ruby/test_integer.rb	(revision 56896)
+++ test/ruby/test_integer.rb	(revision 56897)
@@ -197,6 +197,10 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L197
     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, +249.round(-2, half: :down))
+    assert_int_equal(+200, +250.round(-2, half: :down))
+    assert_int_equal(+300, +349.round(-2, half: :down))
+    assert_int_equal(+300, +350.round(-2, half: :down))
     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))
@@ -206,6 +210,10 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L210
     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, -249.round(-2, half: :down))
+    assert_int_equal(-200, -250.round(-2, half: :down))
+    assert_int_equal(-300, -349.round(-2, half: :down))
+    assert_int_equal(-300, -350.round(-2, half: :down))
     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))
@@ -230,6 +238,14 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L238
     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(+20 * 10**70, (+25 * 10**70).round(-71, half: :down))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :down))
+    assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :down))
+    assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :down))
+    assert_int_equal(+30 * 10**70, (+35 * 10**70).round(-71, half: :down))
+    assert_int_equal(-30 * 10**70, (-35 * 10**70).round(-71, half: :down))
+    assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :down))
+    assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :down))
 
     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/ruby/test_float.rb
===================================================================
--- test/ruby/test_float.rb	(revision 56896)
+++ test/ruby/test_float.rb	(revision 56897)
@@ -701,6 +701,27 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L701
     assert_equal(-7.1364, -7.1364499.round(4, half: :up))
   end
 
+  def test_round_half_down
+    assert_equal(12.0, 12.5.round(half: :down))
+    assert_equal(13.0, 13.5.round(half: :down))
+
+    assert_equal(2.1, 2.15.round(1, half: :down))
+    assert_equal(2.2, 2.25.round(1, half: :down))
+    assert_equal(2.3, 2.35.round(1, half: :down))
+
+    assert_equal(-2.1, -2.15.round(1, half: :down))
+    assert_equal(-2.2, -2.25.round(1, half: :down))
+    assert_equal(-2.3, -2.35.round(1, half: :down))
+
+    assert_equal(7.1364, 7.13645.round(4, half: :down))
+    assert_equal(7.1365, 7.1364501.round(4, half: :down))
+    assert_equal(7.1364, 7.1364499.round(4, half: :down))
+
+    assert_equal(-7.1364, -7.13645.round(4, half: :down))
+    assert_equal(-7.1365, -7.1364501.round(4, half: :down))
+    assert_equal(-7.1364, -7.1364499.round(4, half: :down))
+  end
+
   def test_round_half_invalid
     assert_raise_with_message(ArgumentError, /xxx/) {
       1.0.round(half: "\0xxx")
Index: test/test_mathn.rb
===================================================================
--- test/test_mathn.rb	(revision 56896)
+++ test/test_mathn.rb	(revision 56897)
@@ -157,6 +157,27 @@ class TestMathn < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_mathn.rb#L157
       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))
+
+      assert_equal( 3, ( 13/5).round(half: :down))
+      assert_equal( 2, (  5/2).round(half: :down))
+      assert_equal( 2, ( 12/5).round(half: :down))
+      assert_equal(-2, (-12/5).round(half: :down))
+      assert_equal(-2, ( -5/2).round(half: :down))
+      assert_equal(-3, (-13/5).round(half: :down))
+
+      assert_equal( 3, ( 13/5).round(0, half: :down))
+      assert_equal( 2, (  5/2).round(0, half: :down))
+      assert_equal( 2, ( 12/5).round(0, half: :down))
+      assert_equal(-2, (-12/5).round(0, half: :down))
+      assert_equal(-2, ( -5/2).round(0, half: :down))
+      assert_equal(-3, (-13/5).round(0, half: :down))
+
+      assert_equal(( 13/5), ( 13/5).round(2, half: :down))
+      assert_equal((  5/2), (  5/2).round(2, half: :down))
+      assert_equal(( 12/5), ( 12/5).round(2, half: :down))
+      assert_equal((-12/5), (-12/5).round(2, half: :down))
+      assert_equal(( -5/2), ( -5/2).round(2, half: :down))
+      assert_equal((-13/5), (-13/5).round(2, half: :down))
     EOS
   end
 end
Index: rational.c
===================================================================
--- rational.c	(revision 56896)
+++ rational.c	(revision 56897)
@@ -1324,6 +1324,31 @@ nurat_round_half_up(VALUE self) https://github.com/ruby/ruby/blob/trunk/rational.c#L1324
 }
 
 static VALUE
+nurat_round_half_down(VALUE self)
+{
+    VALUE num, den, neg;
+
+    get_dat1(self);
+
+    num = dat->num;
+    den = dat->den;
+    neg = INT_NEGATIVE_P(num);
+
+    if (neg)
+	num = rb_int_uminus(num);
+
+    num = rb_int_plus(rb_int_mul(num, TWO), den);
+    num = rb_int_minus(num, ONE);
+    den = rb_int_mul(den, TWO);
+    num = rb_int_idiv(num, den);
+
+    if (neg)
+	num = rb_int_uminus(num);
+
+    return num;
+}
+
+static VALUE
 nurat_round_half_even(VALUE self)
 {
     VALUE num, den, neg, qr;
Index: numeric.c
===================================================================
--- numeric.c	(revision 56896)
+++ numeric.c	(revision 56897)
@@ -119,6 +119,31 @@ round_half_up(double x, double s) https://github.com/ruby/ruby/blob/trunk/numeric.c#L119
 }
 
 static double
+round_half_down(double x, double s)
+{
+    double f, xs = x * s;
+
+#ifdef HAVE_ROUND
+    f = round(xs);
+#endif
+    if (x > 0) {
+#ifndef HAVE_ROUND
+	f = ceil(xs);
+#endif
+	if ((double)((f - 0.5) / s) >= x) f -= 1;
+	x = f;
+    }
+    else {
+#ifndef HAVE_ROUND
+	f = floor(xs);
+#endif
+	if ((double)((f + 0.5) / s) <= x) f += 1;
+	x = f;
+    }
+    return x;
+}
+
+static double
 round_half_even(double x, double s)
 {
     double f, d, xs = x * s;
@@ -213,6 +238,8 @@ rb_num_get_rounding_option(VALUE opts) https://github.com/ruby/ruby/blob/trunk/numeric.c#L238
 	  case 4:
 	    if (rb_memcicmp(s, "even", 4) == 0)
 		return RUBY_NUM_ROUND_HALF_EVEN;
+	    if (strncasecmp(s, "down", 4) == 0)
+		return RUBY_NUM_ROUND_HALF_DOWN;
 	    break;
 	}
       invalid:
@@ -2040,6 +2067,12 @@ int_round_half_up(SIGNED_VALUE x, SIGNED https://github.com/ruby/ruby/blob/trunk/numeric.c#L2067
     return (x + y / 2) / y * y;
 }
 
+static SIGNED_VALUE
+int_round_half_down(SIGNED_VALUE x, SIGNED_VALUE y)
+{
+    return (x + y / 2 - 1) / y * y;
+}
+
 static int
 int_half_p_half_even(VALUE num, VALUE n, VALUE f)
 {
@@ -2052,6 +2085,12 @@ int_half_p_half_up(VALUE num, VALUE n, V https://github.com/ruby/ruby/blob/trunk/numeric.c#L2085
     return int_pos_p(num);
 }
 
+static int
+int_half_p_half_down(VALUE num, VALUE n, VALUE f)
+{
+    return int_neg_p(num);
+}
+
 /*
  * Assumes num is an Integer, ndigits <= 0
  */

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

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