aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--NEWS11
-rw-r--r--internal.h14
-rw-r--r--lib/rexml/functions.rb11
-rw-r--r--numeric.c121
-rw-r--r--rational.c38
-rw-r--r--test/ruby/test_float.rb42
-rw-r--r--test/ruby/test_integer.rb44
-rw-r--r--test/ruby/test_rational.rb23
-rw-r--r--test/test_mathn.rb50
10 files changed, 322 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index 71133697d2..bea8496f56 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sat Nov 5 18:49:37 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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 <knu@iDaemons.org>
* lib/set.rb (Set#compare_by_identity, Set#compare_by_identity?):
diff --git a/NEWS b/NEWS
index d9f6c6eedb..3f4c64b8b1 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/numeric.c b/numeric.c
index 00a1050f43..4881f75217 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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