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>
|
Sat Nov 5 18:17:54 2016 Akinori MUSHA <knu@iDaemons.org>
|
||||||
|
|
||||||
* lib/set.rb (Set#compare_by_identity, Set#compare_by_identity?):
|
* 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
|
* Float#ceil, Float#floor, and Float#truncate now take an optional
|
||||||
digits, as well as Float#round. [Feature #12245]
|
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
|
||||||
|
|
||||||
* Hash#transform_values and Hash#transform_values! [Feature #12512]
|
* 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]
|
* 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
|
||||||
|
|
||||||
* IO#gets, IO#readline, IO#each_line, IO#readlines, IO#foreach now takes
|
* 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
|
* Support CLOCK_MONOTONIC_RAW_APPROX, CLOCK_UPTIME_RAW, and
|
||||||
CLOCK_UPTIME_RAW_APPROX which are introduced by macOS 10.12.
|
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
|
||||||
|
|
||||||
* Regexp#match? [Feature #8110]
|
* Regexp#match? [Feature #8110]
|
||||||
|
|
14
internal.h
14
internal.h
|
@ -1135,6 +1135,17 @@ VALUE rb_math_sqrt(VALUE);
|
||||||
void Init_newline(void);
|
void Init_newline(void);
|
||||||
|
|
||||||
/* numeric.c */
|
/* 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);
|
int rb_num_to_uint(VALUE val, unsigned int *ret);
|
||||||
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
|
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);
|
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_mul(VALUE x, VALUE y);
|
||||||
VALUE rb_int_idiv(VALUE x, VALUE y);
|
VALUE rb_int_idiv(VALUE x, VALUE y);
|
||||||
VALUE rb_int_modulo(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_int2str(VALUE num, int base);
|
||||||
VALUE rb_dbl_hash(double d);
|
VALUE rb_dbl_hash(double d);
|
||||||
VALUE rb_fix_plus(VALUE x, VALUE y);
|
VALUE rb_fix_plus(VALUE x, VALUE y);
|
||||||
VALUE rb_int_ge(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
|
#if USE_FLONUM
|
||||||
#define RUBY_BIT_ROTL(v, n) (((v) << (n)) | ((v) >> ((sizeof(v) * 8) - n)))
|
#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
|
# 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.
|
# are 0..length. Therefore, we have to offset the bounds by one.
|
||||||
ruby_start = ruby_start.round - 1
|
ruby_start = round(ruby_start) - 1
|
||||||
ruby_length = ruby_length.round
|
ruby_length = round(ruby_length)
|
||||||
|
|
||||||
if ruby_start < 0
|
if ruby_start < 0
|
||||||
ruby_length += ruby_start unless infinite_length
|
ruby_length += ruby_start unless infinite_length
|
||||||
|
@ -376,10 +376,13 @@ module REXML
|
||||||
end
|
end
|
||||||
|
|
||||||
def Functions::round( number )
|
def Functions::round( number )
|
||||||
|
number = number(number)
|
||||||
begin
|
begin
|
||||||
number(number).round
|
neg = number.negative?
|
||||||
|
number = number.abs.round(half: :up)
|
||||||
|
neg ? -number : number
|
||||||
rescue FloatDomainError
|
rescue FloatDomainError
|
||||||
number(number)
|
number
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
121
numeric.c
121
numeric.c
|
@ -93,7 +93,7 @@ round(double x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static double
|
static double
|
||||||
round_to_nearest(double x, double s)
|
round_half_up(double x, double s)
|
||||||
{
|
{
|
||||||
double f, xs = x * s;
|
double f, xs = x * s;
|
||||||
|
|
||||||
|
@ -117,12 +117,44 @@ round_to_nearest(double x, double s)
|
||||||
return x;
|
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_uminus(VALUE num);
|
||||||
static VALUE fix_mul(VALUE x, VALUE y);
|
static VALUE fix_mul(VALUE x, VALUE y);
|
||||||
static VALUE fix_lshift(long, unsigned long);
|
static VALUE fix_lshift(long, unsigned long);
|
||||||
static VALUE fix_rshift(long, unsigned long);
|
static VALUE fix_rshift(long, unsigned long);
|
||||||
static VALUE int_pow(long x, unsigned long y);
|
static VALUE int_pow(long x, unsigned long y);
|
||||||
static VALUE int_cmp(VALUE x, VALUE 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);
|
static int int_round_zero_p(VALUE num, int ndigits);
|
||||||
VALUE rb_int_floor(VALUE num, int ndigits);
|
VALUE rb_int_floor(VALUE num, int ndigits);
|
||||||
VALUE rb_int_ceil(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");
|
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 */
|
/* experimental API */
|
||||||
int
|
int
|
||||||
rb_num_to_uint(VALUE val, unsigned int *ret)
|
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_NEGATIVE_P(num) ((SIGNED_VALUE)(num) < 0)
|
||||||
#define FIXNUM_ZERO_P(num) ((num) == INT2FIX(0))
|
#define FIXNUM_ZERO_P(num) ((num) == INT2FIX(0))
|
||||||
|
|
||||||
#if 0
|
|
||||||
static inline int
|
static inline int
|
||||||
int_pos_p(VALUE num)
|
int_pos_p(VALUE num)
|
||||||
{
|
{
|
||||||
|
@ -213,7 +276,6 @@ int_pos_p(VALUE num)
|
||||||
}
|
}
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
int_neg_p(VALUE num)
|
int_neg_p(VALUE num)
|
||||||
|
@ -1962,11 +2024,27 @@ int_round_zero_p(VALUE num, int ndigits)
|
||||||
return (-0.415241 * ndigits - 0.125 > bytes);
|
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
|
* Assumes num is an Integer, ndigits <= 0
|
||||||
*/
|
*/
|
||||||
VALUE
|
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;
|
VALUE n, f, h, r;
|
||||||
|
|
||||||
|
@ -1979,7 +2057,9 @@ rb_int_round(VALUE num, int ndigits)
|
||||||
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
|
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
|
||||||
int neg = x < 0;
|
int neg = x < 0;
|
||||||
if (neg) x = -x;
|
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;
|
if (neg) x = -x;
|
||||||
return LONG2NUM(x);
|
return LONG2NUM(x);
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +2071,11 @@ rb_int_round(VALUE num, int ndigits)
|
||||||
r = rb_int_modulo(num, f);
|
r = rb_int_modulo(num, f);
|
||||||
n = rb_int_minus(num, r);
|
n = rb_int_minus(num, r);
|
||||||
r = int_cmp(r, h);
|
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);
|
n = rb_int_plus(n, f);
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
|
@ -2109,21 +2193,27 @@ static VALUE
|
||||||
flo_round(int argc, VALUE *argv, VALUE num)
|
flo_round(int argc, VALUE *argv, VALUE num)
|
||||||
{
|
{
|
||||||
double number, f, x;
|
double number, f, x;
|
||||||
|
VALUE nd, opt;
|
||||||
int ndigits = 0;
|
int ndigits = 0;
|
||||||
|
enum ruby_num_rounding_mode mode;
|
||||||
|
|
||||||
if (rb_check_arity(argc, 0, 1)) {
|
if (rb_scan_args(argc, argv, "01:", &nd, &opt)) {
|
||||||
ndigits = NUM2INT(argv[0]);
|
ndigits = NUM2INT(nd);
|
||||||
}
|
}
|
||||||
|
mode = rb_num_get_rounding_option(opt);
|
||||||
if (ndigits < 0) {
|
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);
|
number = RFLOAT_VALUE(num);
|
||||||
if (ndigits == 0) {
|
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;
|
if (float_invariant_round(number, ndigits, &num)) return num;
|
||||||
f = pow(10, ndigits);
|
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);
|
return DBL2NUM(x / f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4862,16 +4952,19 @@ static VALUE
|
||||||
int_round(int argc, VALUE* argv, VALUE num)
|
int_round(int argc, VALUE* argv, VALUE num)
|
||||||
{
|
{
|
||||||
int ndigits;
|
int ndigits;
|
||||||
|
int mode;
|
||||||
|
VALUE nd, opt;
|
||||||
|
|
||||||
if (!rb_check_arity(argc, 0, 1)) return num;
|
if (!rb_scan_args(argc, argv, "01:", &nd, &opt)) return num;
|
||||||
ndigits = NUM2INT(argv[0]);
|
ndigits = NUM2INT(nd);
|
||||||
|
mode = rb_num_get_rounding_option(opt);
|
||||||
if (ndigits > 0) {
|
if (ndigits > 0) {
|
||||||
return rb_Float(num);
|
return rb_Float(num);
|
||||||
}
|
}
|
||||||
if (ndigits == 0) {
|
if (ndigits == 0) {
|
||||||
return num;
|
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
|
static VALUE
|
||||||
nurat_round(VALUE self)
|
nurat_round_half_up(VALUE self)
|
||||||
{
|
{
|
||||||
VALUE num, den, neg;
|
VALUE num, den, neg;
|
||||||
|
|
||||||
|
@ -1273,6 +1273,33 @@ nurat_round(VALUE self)
|
||||||
return num;
|
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
|
static VALUE
|
||||||
f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(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
|
static VALUE
|
||||||
nurat_round_n(int argc, VALUE *argv, VALUE self)
|
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
|
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
|
def test_Float
|
||||||
assert_in_delta(0.125, Float("0.1_2_5"), 0.00001)
|
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)
|
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(11110, 11111.round(-1))
|
||||||
assert_int_equal(11100, 11111.round(-2))
|
assert_int_equal(11100, 11111.round(-2))
|
||||||
assert_int_equal(+200, +249.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(-200, -249.round(-2))
|
||||||
assert_int_equal(-300, -250.round(-2))
|
assert_int_equal(+200, +249.round(-2, half: :even))
|
||||||
assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71))
|
assert_int_equal(+200, +250.round(-2, half: :even))
|
||||||
assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71))
|
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(-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))
|
||||||
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
|
end
|
||||||
|
|
||||||
def test_trunc
|
def test_trunc
|
||||||
[[Rational(13, 5), [ 2, 3, 2, 3]], # 2.6
|
[[Rational(13, 5), [ 2, 3, 2, 3, 3, 3]], # 2.6
|
||||||
[Rational(5, 2), [ 2, 3, 2, 3]], # 2.5
|
[Rational(5, 2), [ 2, 3, 2, 2, 2, 3]], # 2.5
|
||||||
[Rational(12, 5), [ 2, 3, 2, 2]], # 2.4
|
[Rational(12, 5), [ 2, 3, 2, 2, 2, 2]], # 2.4
|
||||||
[Rational(-12,5), [-3, -2, -2, -2]], # -2.4
|
[Rational(-12,5), [-3, -2, -2, -2, -2, -2]], # -2.4
|
||||||
[Rational(-5, 2), [-3, -2, -2, -3]], # -2.5
|
[Rational(-5, 2), [-3, -2, -2, -2, -2, -3]], # -2.5
|
||||||
[Rational(-13, 5), [-3, -2, -2, -3]], # -2.6
|
[Rational(-13, 5), [-3, -2, -2, -3, -3, -3]], # -2.6
|
||||||
].each do |i, a|
|
].each do |i, a|
|
||||||
assert_equal(a[0], i.floor)
|
s = proc {i.inspect}
|
||||||
assert_equal(a[1], i.ceil)
|
assert_equal(a[0], i.floor, s)
|
||||||
assert_equal(a[2], i.truncate)
|
assert_equal(a[1], i.ceil, s)
|
||||||
assert_equal(a[3], i.round)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -96,17 +96,17 @@ class TestMathn < Test::Unit::TestCase
|
||||||
def test_round
|
def test_round
|
||||||
assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
|
assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
|
||||||
assert_equal( 3, ( 13/5).round)
|
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(-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)
|
||||||
|
|
||||||
assert_equal( 3, ( 13/5).round(0))
|
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(-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(-3, (-13/5).round(0))
|
||||||
|
|
||||||
assert_equal(( 13/5), ( 13/5).round(2))
|
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((-12/5), (-12/5).round(2))
|
||||||
assert_equal(( -5/2), ( -5/2).round(2))
|
assert_equal(( -5/2), ( -5/2).round(2))
|
||||||
assert_equal((-13/5), (-13/5).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
|
EOS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue