summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-08-04 15:29:45 +0000
committernagachika <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-08-04 15:29:45 +0000
commit2072eccda17af1349ef13aa5194e80a5805b3bd3 (patch)
treea2626d4958c0eb6df0d3544e1e1a2e374b9ea25b
parent00992ff8d2da22ab3c005a082ff77cf974207081 (diff)
merge revision(s) 58913: [Backport #13599]
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/branches/ruby_2_4@59505 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--numeric.c84
-rw-r--r--test/ruby/test_float.rb4
-rw-r--r--version.h2
3 files changed, 58 insertions, 32 deletions
diff --git a/numeric.c b/numeric.c
index 1fa4c4d562..f36f3b1163 100644
--- a/numeric.c
+++ b/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
@@ -1945,28 +1946,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);
}
/*
@@ -2006,18 +2009,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
@@ -2241,27 +2253,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
@@ -2280,12 +2298,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;
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index 7be9894a4b..fcff9fc7b8 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -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))
diff --git a/version.h b/version.h
index 484244709f..611904b7d2 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
#define RUBY_VERSION "2.4.2"
#define RUBY_RELEASE_DATE "2017-08-05"
-#define RUBY_PATCHLEVEL 170
+#define RUBY_PATCHLEVEL 171
#define RUBY_RELEASE_YEAR 2017
#define RUBY_RELEASE_MONTH 8