summaryrefslogtreecommitdiff
path: root/rational.c
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-11-05 09:49:39 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-11-05 09:49:39 +0000
commitdfe91fcd08e7e255b5cb1b306e53bea1ddb54e5f (patch)
treed209f0b546e45cb8d8fa7fc2bbbb5044e53e2c8f /rational.c
parent76977611dd68e384fdce8c546efda5e1931e67a6 (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
Diffstat (limited to 'rational.c')
-rw-r--r--rational.c38
1 files changed, 36 insertions, 2 deletions
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);
}
/*