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 /rational.c | |
parent | 76977611dd68e384fdce8c546efda5e1931e67a6 (diff) | |
download | ruby-dfe91fcd08e7e255b5cb1b306e53bea1ddb54e5f.tar.gz |
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.c | 38 |
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); } /* |