зеркало из https://github.com/github/ruby.git
numeric.c: fix for small number
* numeric.c (flo_floor, flo_ceil): should not return zero for small number. [ruby-core:81394] [Bug #13599] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
1dec75c02f
Коммит
9f5a468cf9
84
numeric.c
84
numeric.c
|
@ -168,7 +168,8 @@ 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);
|
||||
static VALUE flo_to_i(VALUE num);
|
||||
static int float_invariant_round(double number, int ndigits, VALUE *num);
|
||||
static int float_round_overflow(int ndigits, int binexp);
|
||||
static int float_round_underflow(int ndigits, int binexp);
|
||||
|
||||
static ID id_coerce, id_div, id_divmod;
|
||||
#define id_to_i idTo_i
|
||||
|
@ -1933,28 +1934,30 @@ static VALUE
|
|||
flo_floor(int argc, VALUE *argv, VALUE num)
|
||||
{
|
||||
double number, f;
|
||||
long val;
|
||||
int ndigits = 0;
|
||||
|
||||
if (rb_check_arity(argc, 0, 1)) {
|
||||
ndigits = NUM2INT(argv[0]);
|
||||
}
|
||||
if (ndigits < 0) {
|
||||
return rb_int_floor(flo_to_i(num), ndigits);
|
||||
}
|
||||
number = RFLOAT_VALUE(num);
|
||||
if (number == 0.0) {
|
||||
return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
|
||||
}
|
||||
if (ndigits > 0) {
|
||||
if (float_invariant_round(number, ndigits, &num)) return num;
|
||||
int binexp;
|
||||
frexp(number, &binexp);
|
||||
if (float_round_overflow(ndigits, binexp)) return num;
|
||||
if (number > 0.0 && float_round_underflow(ndigits, binexp))
|
||||
return DBL2NUM(0.0);
|
||||
f = pow(10, ndigits);
|
||||
f = floor(number * f) / f;
|
||||
return DBL2NUM(f);
|
||||
}
|
||||
f = floor(number);
|
||||
if (!FIXABLE(f)) {
|
||||
return rb_dbl2big(f);
|
||||
else {
|
||||
num = dbl2ival(floor(number));
|
||||
if (ndigits < 0) num = rb_int_floor(num, ndigits);
|
||||
return num;
|
||||
}
|
||||
val = (long)f;
|
||||
return LONG2FIX(val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2003,18 +2006,27 @@ flo_ceil(int argc, VALUE *argv, VALUE num)
|
|||
int ndigits = 0;
|
||||
|
||||
if (rb_check_arity(argc, 0, 1)) {
|
||||
ndigits = NUM2INT(argv[0]);
|
||||
ndigits = NUM2INT(argv[0]);
|
||||
}
|
||||
number = RFLOAT_VALUE(num);
|
||||
if (ndigits < 0) {
|
||||
return rb_int_ceil(dbl2ival(ceil(number)), ndigits);
|
||||
if (number == 0.0) {
|
||||
return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
|
||||
}
|
||||
if (ndigits == 0) {
|
||||
return dbl2ival(ceil(number));
|
||||
if (ndigits > 0) {
|
||||
int binexp;
|
||||
frexp(number, &binexp);
|
||||
if (float_round_overflow(ndigits, binexp)) return num;
|
||||
if (number < 0.0 && float_round_underflow(ndigits, binexp))
|
||||
return DBL2NUM(0.0);
|
||||
f = pow(10, ndigits);
|
||||
f = ceil(number * f) / f;
|
||||
return DBL2NUM(f);
|
||||
}
|
||||
else {
|
||||
num = dbl2ival(ceil(number));
|
||||
if (ndigits < 0) num = rb_int_ceil(num, ndigits);
|
||||
return num;
|
||||
}
|
||||
if (float_invariant_round(number, ndigits, &num)) return num;
|
||||
f = pow(10, ndigits);
|
||||
return DBL2NUM(ceil(number * f) / f);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2252,27 +2264,33 @@ flo_round(int argc, VALUE *argv, VALUE num)
|
|||
ndigits = NUM2INT(nd);
|
||||
}
|
||||
mode = rb_num_get_rounding_option(opt);
|
||||
number = RFLOAT_VALUE(num);
|
||||
if (number == 0.0) {
|
||||
return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
|
||||
}
|
||||
if (ndigits < 0) {
|
||||
return rb_int_round(flo_to_i(num), ndigits, mode);
|
||||
}
|
||||
number = RFLOAT_VALUE(num);
|
||||
if (ndigits == 0) {
|
||||
x = ROUND_CALL(mode, round, (number, 1.0));
|
||||
return dbl2ival(x);
|
||||
}
|
||||
if (float_invariant_round(number, ndigits, &num)) return num;
|
||||
f = pow(10, ndigits);
|
||||
x = ROUND_CALL(mode, round, (number, f));
|
||||
return DBL2NUM(x / f);
|
||||
if (isfinite(number)) {
|
||||
int binexp;
|
||||
frexp(number, &binexp);
|
||||
if (float_round_overflow(ndigits, binexp)) return num;
|
||||
if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0);
|
||||
f = pow(10, ndigits);
|
||||
x = ROUND_CALL(mode, round, (number, f));
|
||||
return DBL2NUM(x / f);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static int
|
||||
float_invariant_round(double number, int ndigits, VALUE *num)
|
||||
float_round_overflow(int ndigits, int binexp)
|
||||
{
|
||||
enum {float_dig = DBL_DIG+2};
|
||||
int binexp;
|
||||
|
||||
frexp(number, &binexp);
|
||||
|
||||
/* Let `exp` be such that `number` is written as:"0.#{digits}e#{exp}",
|
||||
i.e. such that 10 ** (exp - 1) <= |number| < 10 ** exp
|
||||
|
@ -2291,12 +2309,16 @@ float_invariant_round(double number, int ndigits, VALUE *num)
|
|||
So if ndigits + floor(binexp/(4 or 3)) >= float_dig, the result is number
|
||||
If ndigits + ceil(binexp/(3 or 4)) < 0 the result is 0
|
||||
*/
|
||||
if (isinf(number) || isnan(number) ||
|
||||
(ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1))) {
|
||||
if (ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
float_round_underflow(int ndigits, int binexp)
|
||||
{
|
||||
if (ndigits < - (binexp > 0 ? binexp / 3 + 1 : binexp / 4)) {
|
||||
*num = DBL2NUM(0);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
|
|
|
@ -457,6 +457,8 @@ class TestFloat < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_floor_with_precision
|
||||
assert_equal(+0.0, +0.001.floor(1))
|
||||
assert_equal(-0.1, -0.001.floor(1))
|
||||
assert_equal(1.100, 1.111.floor(1))
|
||||
assert_equal(1.110, 1.111.floor(2))
|
||||
assert_equal(11110, 11119.9.floor(-1))
|
||||
|
@ -484,6 +486,8 @@ class TestFloat < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_ceil_with_precision
|
||||
assert_equal(+0.1, +0.001.ceil(1))
|
||||
assert_equal(-0.0, -0.001.ceil(1))
|
||||
assert_equal(1.200, 1.111.ceil(1))
|
||||
assert_equal(1.120, 1.111.ceil(2))
|
||||
assert_equal(11120, 11111.1.ceil(-1))
|
||||
|
|
Загрузка…
Ссылка в новой задаче