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/