round-down
* numeric.c (round_half_down, int_round_half_down): support round-down mode. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56897 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b6d10b6c37
commit
631dde2572
@ -1149,16 +1149,17 @@ void Init_newline(void);
|
|||||||
enum ruby_num_rounding_mode {
|
enum ruby_num_rounding_mode {
|
||||||
RUBY_NUM_ROUND_HALF_UP,
|
RUBY_NUM_ROUND_HALF_UP,
|
||||||
RUBY_NUM_ROUND_HALF_EVEN,
|
RUBY_NUM_ROUND_HALF_EVEN,
|
||||||
|
RUBY_NUM_ROUND_HALF_DOWN,
|
||||||
RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT
|
RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT
|
||||||
};
|
};
|
||||||
#define ROUND_TO(mode, even, up) \
|
#define ROUND_TO(mode, even, up, down) \
|
||||||
((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
|
((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
|
||||||
up)
|
(mode) == RUBY_NUM_ROUND_HALF_UP ? up : down)
|
||||||
#define ROUND_FUNC(mode, name) \
|
#define ROUND_FUNC(mode, name) \
|
||||||
ROUND_TO(mode, name##_half_even, name##_half_up)
|
ROUND_TO(mode, name##_half_even, name##_half_up, name##_half_down)
|
||||||
#define ROUND_CALL(mode, name, args) \
|
#define ROUND_CALL(mode, name, args) \
|
||||||
ROUND_TO(mode, name##_half_even args, \
|
ROUND_TO(mode, name##_half_even args, \
|
||||||
name##_half_up args)
|
name##_half_up args, name##_half_down args)
|
||||||
|
|
||||||
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);
|
||||||
|
39
numeric.c
39
numeric.c
@ -118,6 +118,31 @@ round_half_up(double x, double s)
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
round_half_down(double x, double s)
|
||||||
|
{
|
||||||
|
double f, xs = x * s;
|
||||||
|
|
||||||
|
#ifdef HAVE_ROUND
|
||||||
|
f = round(xs);
|
||||||
|
#endif
|
||||||
|
if (x > 0) {
|
||||||
|
#ifndef HAVE_ROUND
|
||||||
|
f = ceil(xs);
|
||||||
|
#endif
|
||||||
|
if ((double)((f - 0.5) / s) >= x) f -= 1;
|
||||||
|
x = f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#ifndef HAVE_ROUND
|
||||||
|
f = floor(xs);
|
||||||
|
#endif
|
||||||
|
if ((double)((f + 0.5) / s) <= x) f += 1;
|
||||||
|
x = f;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
round_half_even(double x, double s)
|
round_half_even(double x, double s)
|
||||||
{
|
{
|
||||||
@ -213,6 +238,8 @@ rb_num_get_rounding_option(VALUE opts)
|
|||||||
case 4:
|
case 4:
|
||||||
if (rb_memcicmp(s, "even", 4) == 0)
|
if (rb_memcicmp(s, "even", 4) == 0)
|
||||||
return RUBY_NUM_ROUND_HALF_EVEN;
|
return RUBY_NUM_ROUND_HALF_EVEN;
|
||||||
|
if (strncasecmp(s, "down", 4) == 0)
|
||||||
|
return RUBY_NUM_ROUND_HALF_DOWN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
invalid:
|
invalid:
|
||||||
@ -2040,6 +2067,12 @@ int_round_half_up(SIGNED_VALUE x, SIGNED_VALUE y)
|
|||||||
return (x + y / 2) / y * y;
|
return (x + y / 2) / y * y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SIGNED_VALUE
|
||||||
|
int_round_half_down(SIGNED_VALUE x, SIGNED_VALUE y)
|
||||||
|
{
|
||||||
|
return (x + y / 2 - 1) / y * y;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
int_half_p_half_even(VALUE num, VALUE n, VALUE f)
|
int_half_p_half_even(VALUE num, VALUE n, VALUE f)
|
||||||
{
|
{
|
||||||
@ -2052,6 +2085,12 @@ int_half_p_half_up(VALUE num, VALUE n, VALUE f)
|
|||||||
return int_pos_p(num);
|
return int_pos_p(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
int_half_p_half_down(VALUE num, VALUE n, VALUE f)
|
||||||
|
{
|
||||||
|
return int_neg_p(num);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assumes num is an Integer, ndigits <= 0
|
* Assumes num is an Integer, ndigits <= 0
|
||||||
*/
|
*/
|
||||||
|
25
rational.c
25
rational.c
@ -1323,6 +1323,31 @@ nurat_round_half_up(VALUE self)
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
nurat_round_half_down(VALUE self)
|
||||||
|
{
|
||||||
|
VALUE num, den, neg;
|
||||||
|
|
||||||
|
get_dat1(self);
|
||||||
|
|
||||||
|
num = dat->num;
|
||||||
|
den = dat->den;
|
||||||
|
neg = INT_NEGATIVE_P(num);
|
||||||
|
|
||||||
|
if (neg)
|
||||||
|
num = rb_int_uminus(num);
|
||||||
|
|
||||||
|
num = rb_int_plus(rb_int_mul(num, TWO), den);
|
||||||
|
num = rb_int_minus(num, ONE);
|
||||||
|
den = rb_int_mul(den, TWO);
|
||||||
|
num = rb_int_idiv(num, den);
|
||||||
|
|
||||||
|
if (neg)
|
||||||
|
num = rb_int_uminus(num);
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
nurat_round_half_even(VALUE self)
|
nurat_round_half_even(VALUE self)
|
||||||
{
|
{
|
||||||
|
@ -701,6 +701,27 @@ class TestFloat < Test::Unit::TestCase
|
|||||||
assert_equal(-7.1364, -7.1364499.round(4, half: :up))
|
assert_equal(-7.1364, -7.1364499.round(4, half: :up))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_round_half_down
|
||||||
|
assert_equal(12.0, 12.5.round(half: :down))
|
||||||
|
assert_equal(13.0, 13.5.round(half: :down))
|
||||||
|
|
||||||
|
assert_equal(2.1, 2.15.round(1, half: :down))
|
||||||
|
assert_equal(2.2, 2.25.round(1, half: :down))
|
||||||
|
assert_equal(2.3, 2.35.round(1, half: :down))
|
||||||
|
|
||||||
|
assert_equal(-2.1, -2.15.round(1, half: :down))
|
||||||
|
assert_equal(-2.2, -2.25.round(1, half: :down))
|
||||||
|
assert_equal(-2.3, -2.35.round(1, half: :down))
|
||||||
|
|
||||||
|
assert_equal(7.1364, 7.13645.round(4, half: :down))
|
||||||
|
assert_equal(7.1365, 7.1364501.round(4, half: :down))
|
||||||
|
assert_equal(7.1364, 7.1364499.round(4, half: :down))
|
||||||
|
|
||||||
|
assert_equal(-7.1364, -7.13645.round(4, half: :down))
|
||||||
|
assert_equal(-7.1365, -7.1364501.round(4, half: :down))
|
||||||
|
assert_equal(-7.1364, -7.1364499.round(4, half: :down))
|
||||||
|
end
|
||||||
|
|
||||||
def test_round_half_invalid
|
def test_round_half_invalid
|
||||||
assert_raise_with_message(ArgumentError, /xxx/) {
|
assert_raise_with_message(ArgumentError, /xxx/) {
|
||||||
1.0.round(half: "\0xxx")
|
1.0.round(half: "\0xxx")
|
||||||
|
@ -197,6 +197,10 @@ class TestInteger < Test::Unit::TestCase
|
|||||||
assert_int_equal(+300, +250.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(+300, +349.round(-2, half: :up))
|
||||||
assert_int_equal(+400, +350.round(-2, half: :up))
|
assert_int_equal(+400, +350.round(-2, half: :up))
|
||||||
|
assert_int_equal(+200, +249.round(-2, half: :down))
|
||||||
|
assert_int_equal(+200, +250.round(-2, half: :down))
|
||||||
|
assert_int_equal(+300, +349.round(-2, half: :down))
|
||||||
|
assert_int_equal(+300, +350.round(-2, half: :down))
|
||||||
assert_int_equal(-200, -250.round(-2))
|
assert_int_equal(-200, -250.round(-2))
|
||||||
assert_int_equal(-200, -249.round(-2, half: :even))
|
assert_int_equal(-200, -249.round(-2, half: :even))
|
||||||
assert_int_equal(-200, -250.round(-2, half: :even))
|
assert_int_equal(-200, -250.round(-2, half: :even))
|
||||||
@ -206,6 +210,10 @@ class TestInteger < Test::Unit::TestCase
|
|||||||
assert_int_equal(-300, -250.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(-300, -349.round(-2, half: :up))
|
||||||
assert_int_equal(-400, -350.round(-2, half: :up))
|
assert_int_equal(-400, -350.round(-2, half: :up))
|
||||||
|
assert_int_equal(-200, -249.round(-2, half: :down))
|
||||||
|
assert_int_equal(-200, -250.round(-2, half: :down))
|
||||||
|
assert_int_equal(-300, -349.round(-2, half: :down))
|
||||||
|
assert_int_equal(-300, -350.round(-2, half: :down))
|
||||||
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).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))
|
||||||
@ -230,6 +238,14 @@ class TestInteger < Test::Unit::TestCase
|
|||||||
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(-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(+20 * 10**70, (+25 * 10**70).round(-71, half: :down))
|
||||||
|
assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :down))
|
||||||
|
assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :down))
|
||||||
|
assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :down))
|
||||||
|
assert_int_equal(+30 * 10**70, (+35 * 10**70).round(-71, half: :down))
|
||||||
|
assert_int_equal(-30 * 10**70, (-35 * 10**70).round(-71, half: :down))
|
||||||
|
assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :down))
|
||||||
|
assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :down))
|
||||||
|
|
||||||
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,12 +597,12 @@ class Rational_Test < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_trunc
|
def test_trunc
|
||||||
[[Rational(13, 5), [ 2, 3, 2, 3, 3, 3]], # 2.6
|
[[Rational(13, 5), [ 2, 3, 2, 3, 3, 3, 3]], # 2.6
|
||||||
[Rational(5, 2), [ 2, 3, 2, 2, 2, 3]], # 2.5
|
[Rational(5, 2), [ 2, 3, 2, 2, 2, 3, 2]], # 2.5
|
||||||
[Rational(12, 5), [ 2, 3, 2, 2, 2, 2]], # 2.4
|
[Rational(12, 5), [ 2, 3, 2, 2, 2, 2, 2]], # 2.4
|
||||||
[Rational(-12,5), [-3, -2, -2, -2, -2, -2]], # -2.4
|
[Rational(-12,5), [-3, -2, -2, -2, -2, -2, -2]], # -2.4
|
||||||
[Rational(-5, 2), [-3, -2, -2, -2, -2, -3]], # -2.5
|
[Rational(-5, 2), [-3, -2, -2, -2, -2, -3, -2]], # -2.5
|
||||||
[Rational(-13, 5), [-3, -2, -2, -3, -3, -3]], # -2.6
|
[Rational(-13, 5), [-3, -2, -2, -3, -3, -3, -3]], # -2.6
|
||||||
].each do |i, a|
|
].each do |i, a|
|
||||||
s = proc {i.inspect}
|
s = proc {i.inspect}
|
||||||
assert_equal(a[0], i.floor, s)
|
assert_equal(a[0], i.floor, s)
|
||||||
@ -611,6 +611,7 @@ class Rational_Test < Test::Unit::TestCase
|
|||||||
assert_equal(a[3], i.round, s)
|
assert_equal(a[3], i.round, s)
|
||||||
assert_equal(a[4], i.round(half: :even), s)
|
assert_equal(a[4], i.round(half: :even), s)
|
||||||
assert_equal(a[5], i.round(half: :up), s)
|
assert_equal(a[5], i.round(half: :up), s)
|
||||||
|
assert_equal(a[6], i.round(half: :down), s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -157,6 +157,27 @@ class TestMathn < Test::Unit::TestCase
|
|||||||
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(( -5/2), ( -5/2).round(2, half: :up))
|
||||||
assert_equal((-13/5), (-13/5).round(2, half: :up))
|
assert_equal((-13/5), (-13/5).round(2, half: :up))
|
||||||
|
|
||||||
|
assert_equal( 3, ( 13/5).round(half: :down))
|
||||||
|
assert_equal( 2, ( 5/2).round(half: :down))
|
||||||
|
assert_equal( 2, ( 12/5).round(half: :down))
|
||||||
|
assert_equal(-2, (-12/5).round(half: :down))
|
||||||
|
assert_equal(-2, ( -5/2).round(half: :down))
|
||||||
|
assert_equal(-3, (-13/5).round(half: :down))
|
||||||
|
|
||||||
|
assert_equal( 3, ( 13/5).round(0, half: :down))
|
||||||
|
assert_equal( 2, ( 5/2).round(0, half: :down))
|
||||||
|
assert_equal( 2, ( 12/5).round(0, half: :down))
|
||||||
|
assert_equal(-2, (-12/5).round(0, half: :down))
|
||||||
|
assert_equal(-2, ( -5/2).round(0, half: :down))
|
||||||
|
assert_equal(-3, (-13/5).round(0, half: :down))
|
||||||
|
|
||||||
|
assert_equal(( 13/5), ( 13/5).round(2, half: :down))
|
||||||
|
assert_equal(( 5/2), ( 5/2).round(2, half: :down))
|
||||||
|
assert_equal(( 12/5), ( 12/5).round(2, half: :down))
|
||||||
|
assert_equal((-12/5), (-12/5).round(2, half: :down))
|
||||||
|
assert_equal(( -5/2), ( -5/2).round(2, half: :down))
|
||||||
|
assert_equal((-13/5), (-13/5).round(2, half: :down))
|
||||||
EOS
|
EOS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user