mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
76977611dd
commit
dfe91fcd08
10 changed files with 322 additions and 39 deletions
|
@ -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?):
|
||||
|
|
11
NEWS
11
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]
|
||||
|
|
14
internal.h
14
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)))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
121
numeric.c
121
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
38
rational.c
38
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;
|
||||
|
||||
|
@ -1273,6 +1273,33 @@ nurat_round(VALUE self)
|
|||
return num;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue