summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <[email protected]>2024-07-26 11:10:39 -0400
committerPeter Zhu <[email protected]>2024-07-30 08:21:28 -0400
commit3af2a7fbe12e11bac7b26bd991d3d02349f47295 (patch)
treea823090e82d8c0dcdbaa248119d3a99c37b887de
parent1870505f478cc75993b296b7144a45137ace6937 (diff)
Fix floor when ndigits is large
[Bug #20654] This commit fixes Integer#floor and Float#floor when the number is negative and ndigits is large such that 10**ndigits is a bignum. Previously, it would return 0 in such cases. However, this would cause unexpected behaviour such as: puts -1.floor(-5) # => -100000 puts -1.floor(-10) # => -10000000000 puts -1.floor(-20) # => 0 This commit changes the last result so that it will return -100000000000000000000.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/11257
-rw-r--r--numeric.c16
-rw-r--r--test/ruby/test_float.rb4
-rw-r--r--test/ruby/test_integer.rb4
3 files changed, 15 insertions, 9 deletions
diff --git a/numeric.c b/numeric.c
index c4ff1143ca..329067189b 100644
--- a/numeric.c
+++ b/numeric.c
@@ -2462,11 +2462,7 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
static VALUE
rb_int_floor(VALUE num, int ndigits)
{
- VALUE f;
-
- if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
- f = int_pow(10, -ndigits);
+ VALUE f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
int neg = x < 0;
@@ -2475,11 +2471,13 @@ rb_int_floor(VALUE num, int ndigits)
if (neg) x = -x;
return LONG2NUM(x);
}
- if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ else {
+ bool neg = int_neg_p(num);
+ if (neg) num = rb_int_minus(rb_int_plus(rb_int_uminus(num), f), INT2FIX(1));
+ num = rb_int_mul(rb_int_div(num, f), f);
+ if (neg) num = rb_int_uminus(num);
+ return num;
}
- return rb_int_minus(num, rb_int_modulo(num, f));
}
static VALUE
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index b91b904d1e..78c37cd3ee 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -530,6 +530,10 @@ class TestFloat < Test::Unit::TestCase
assert_raise(TypeError) {1.0.floor(nil)}
def (prec = Object.new).to_int; 2; end
assert_equal(0.99, 0.998.floor(prec))
+
+ assert_equal(-10000000000, -1.0.floor(-10), "[Bug #20654]")
+ assert_equal(-100000000000000000000, -1.0.floor(-20), "[Bug #20654]")
+ assert_equal(-100000000000000000000000000000000000000000000000000, -1.0.floor(-50), "[Bug #20654]")
end
def test_ceil_with_precision
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 0f33fde78a..3dfd0e2d7b 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -465,6 +465,10 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.floor(1))
assert_int_equal(10**400, (10**400).floor(1))
+
+ assert_int_equal(-10000000000, -1.floor(-10), "[Bug #20654]")
+ assert_int_equal(-100000000000000000000, -1.floor(-20), "[Bug #20654]")
+ assert_int_equal(-100000000000000000000000000000000000000000000000000, -1.floor(-50), "[Bug #20654]")
end
def test_ceil