diff options
author | Peter Zhu <[email protected]> | 2024-07-26 11:10:53 -0400 |
---|---|---|
committer | Peter Zhu <[email protected]> | 2024-07-30 08:21:28 -0400 |
commit | a7167d0ceecc8eea7bdf87e66be16d16b0f417e7 (patch) | |
tree | 8f4b9c2eca4750baa5ef7fe37139e07621235647 | |
parent | 3af2a7fbe12e11bac7b26bd991d3d02349f47295 (diff) |
Fix ceil when ndigits is large
[Bug #20654]
This commit fixes Integer#ceil and Float#ceil 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.ceil(-5) # => 100000
puts 1.ceil(-10) # => 10000000000
puts 1.ceil(-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.c | 19 | ||||
-rw-r--r-- | test/ruby/test_float.rb | 4 | ||||
-rw-r--r-- | test/ruby/test_integer.rb | 4 |
3 files changed, 18 insertions, 9 deletions
@@ -2483,11 +2483,7 @@ rb_int_floor(VALUE num, int ndigits) static VALUE rb_int_ceil(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; @@ -2497,11 +2493,16 @@ rb_int_ceil(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_uminus(num); + else + num = rb_int_plus(num, rb_int_minus(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_plus(num, rb_int_minus(f, rb_int_modulo(num, f))); } VALUE diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index 78c37cd3ee..415d62467e 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -561,6 +561,10 @@ class TestFloat < Test::Unit::TestCase assert_raise(TypeError) {1.0.ceil(nil)} def (prec = Object.new).to_int; 2; end assert_equal(0.99, 0.981.ceil(prec)) + + assert_equal(10000000000, 1.0.ceil(-10), "[Bug #20654]") + assert_equal(100000000000000000000, 1.0.ceil(-20), "[Bug #20654]") + assert_equal(100000000000000000000000000000000000000000000000000, 1.0.ceil(-50), "[Bug #20654]") end def test_truncate_with_precision diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index 3dfd0e2d7b..0312a1a34c 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -525,6 +525,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.truncate(1)) assert_int_equal(10**400, (10**400).truncate(1)) + + assert_int_equal(10000000000, 1.ceil(-10), "[Bug #20654]") + assert_int_equal(100000000000000000000, 1.ceil(-20), "[Bug #20654]") + assert_int_equal(100000000000000000000000000000000000000000000000000, 1.ceil(-50), "[Bug #20654]") end MimicInteger = Struct.new(:to_int) |