diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-11-05 09:49:39 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-11-05 09:49:39 +0000 |
commit | dfe91fcd08e7e255b5cb1b306e53bea1ddb54e5f (patch) | |
tree | d209f0b546e45cb8d8fa7fc2bbbb5044e53e2c8f | |
parent | 76977611dd68e384fdce8c546efda5e1931e67a6 (diff) |
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]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56590 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | NEWS | 11 | ||||
-rw-r--r-- | internal.h | 14 | ||||
-rw-r--r-- | lib/rexml/functions.rb | 11 | ||||
-rw-r--r-- | numeric.c | 121 | ||||
-rw-r--r-- | rational.c | 38 | ||||
-rw-r--r-- | test/ruby/test_float.rb | 42 | ||||
-rw-r--r-- | test/ruby/test_integer.rb | 44 | ||||
-rw-r--r-- | test/ruby/test_rational.rb | 23 | ||||
-rw-r--r-- | test/test_mathn.rb | 50 |
10 files changed, 322 insertions, 39 deletions
@@ -1,3 +1,10 @@ +Sat Nov 5 18:49:37 2016 Nobuyoshi Nakada <[email protected]> + + * 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] + Sat Nov 5 18:17:54 2016 Akinori MUSHA <[email protected]> * lib/set.rb (Set#compare_by_identity, Set#compare_by_identity?): @@ -69,6 +69,9 @@ with all sufficient information, see the ChangeLog file or Redmine * 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 ChangeLog file or Redmine * 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 ChangeLog file or Redmine * 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] diff --git a/internal.h b/internal.h index 847de98eee..7522122844 100644 --- a/internal.h +++ b/internal.h @@ -1135,6 +1135,17 @@ VALUE rb_math_sqrt(VALUE); 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 step, int excl); int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl); @@ -1148,11 +1159,12 @@ VALUE rb_int_minus(VALUE x, VALUE y); VALUE rb_int_mul(VALUE x, VALUE y); VALUE rb_int_idiv(VALUE x, VALUE y); VALUE rb_int_modulo(VALUE x, VALUE y); -VALUE rb_int_round(VALUE num, int ndigits); +VALUE rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode); VALUE rb_int2str(VALUE num, int base); VALUE rb_dbl_hash(double d); VALUE rb_fix_plus(VALUE x, VALUE y); VALUE rb_int_ge(VALUE x, VALUE y); +enum ruby_num_rounding_mode rb_num_get_rounding_option(VALUE opts); #if USE_FLONUM #define RUBY_BIT_ROTL(v, n) (((v) << (n)) | ((v) >> ((sizeof(v) * 8) - n))) diff --git a/lib/rexml/functions.rb b/lib/rexml/functions.rb index ee73b28881..b56103d4f2 100644 --- a/lib/rexml/functions.rb +++ b/lib/rexml/functions.rb @@ -205,8 +205,8 @@ module REXML # 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 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 @@ -93,7 +93,7 @@ round(double x) #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) 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) 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) #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) } return Qnil; } -#endif static inline int int_neg_p(VALUE num) @@ -1962,11 +2024,27 @@ int_round_zero_p(VALUE num, int ndigits) 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) 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) 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 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 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); } /* diff --git a/rational.c b/rational.c index f0e8d56cbf..8362075ab4 100644 --- a/rational.c +++ b/rational.c @@ -1250,7 +1250,7 @@ nurat_truncate(VALUE self) } static VALUE -nurat_round(VALUE self) +nurat_round_half_up(VALUE self) { VALUE num, den, neg; @@ -1274,6 +1274,33 @@ nurat_round(VALUE self) } static VALUE +nurat_round_half_even(VALUE self) +{ + VALUE num, den, neg, qr; + + get_dat1(self); + + num = dat->num; + den = dat->den; + neg = f_negative_p(num); + + if (neg) + num = f_negate(num); + + num = f_add(f_mul(num, TWO), den); + den = f_mul(den, TWO); + qr = rb_funcall(num, rb_intern("divmod"), 1, den); + num = RARRAY_AREF(qr, 0); + if (f_zero_p(RARRAY_AREF(qr, 1))) + num = rb_funcall(num, '&', 1, LONG2FIX(((int)~1))); + + if (neg) + num = f_negate(num); + + return num; +} + +static VALUE f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE)) { VALUE n, b, s; @@ -1403,7 +1430,14 @@ nurat_truncate_n(int argc, VALUE *argv, VALUE self) static VALUE nurat_round_n(int argc, VALUE *argv, VALUE self) { - return f_round_common(argc, argv, self, nurat_round); + VALUE opt; + enum ruby_num_rounding_mode mode = ( + argc = rb_scan_args(argc, argv, "*:", NULL, &opt), + rb_num_get_rounding_option(opt)); + VALUE (*round_func)(VALUE) = + ROUND_TO(mode, + nurat_round_half_up, nurat_round_half_even); + return f_round_common(argc, argv, self, round_func); } /* diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index b31c0415f2..f2989e4f5b 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -659,6 +659,48 @@ class TestFloat < Test::Unit::TestCase } 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) diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index 6e505ec003..d9dd754ca6 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -187,13 +187,49 @@ class TestInteger < Test::Unit::TestCase 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)) diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb index 387f4121cf..d65c292970 100644 --- a/test/ruby/test_rational.rb +++ b/test/ruby/test_rational.rb @@ -597,17 +597,20 @@ class Rational_Test < Test::Unit::TestCase 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 diff --git a/test/test_mathn.rb b/test/test_mathn.rb index aaf132ba88..2ea049502c 100644 --- a/test/test_mathn.rb +++ b/test/test_mathn.rb @@ -96,17 +96,17 @@ class TestMathn < Test::Unit::TestCase 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 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 |