Skip to content

Commit a7167d0

Browse files
committed
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.
1 parent 3af2a7f commit a7167d0

File tree

3 files changed

+18
-9
lines changed

3 files changed

+18
-9
lines changed

numeric.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2483,11 +2483,7 @@ rb_int_floor(VALUE num, int ndigits)
24832483
static VALUE
24842484
rb_int_ceil(VALUE num, int ndigits)
24852485
{
2486-
VALUE f;
2487-
2488-
if (int_round_zero_p(num, ndigits))
2489-
return INT2FIX(0);
2490-
f = int_pow(10, -ndigits);
2486+
VALUE f = int_pow(10, -ndigits);
24912487
if (FIXNUM_P(num) && FIXNUM_P(f)) {
24922488
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
24932489
int neg = x < 0;
@@ -2497,11 +2493,16 @@ rb_int_ceil(VALUE num, int ndigits)
24972493
if (neg) x = -x;
24982494
return LONG2NUM(x);
24992495
}
2500-
if (RB_FLOAT_TYPE_P(f)) {
2501-
/* then int_pow overflow */
2502-
return INT2FIX(0);
2496+
else {
2497+
bool neg = int_neg_p(num);
2498+
if (neg)
2499+
num = rb_int_uminus(num);
2500+
else
2501+
num = rb_int_plus(num, rb_int_minus(f, INT2FIX(1)));
2502+
num = rb_int_mul(rb_int_div(num, f), f);
2503+
if (neg) num = rb_int_uminus(num);
2504+
return num;
25032505
}
2504-
return rb_int_plus(num, rb_int_minus(f, rb_int_modulo(num, f)));
25052506
}
25062507

25072508
VALUE

test/ruby/test_float.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,10 @@ def test_ceil_with_precision
561561
assert_raise(TypeError) {1.0.ceil(nil)}
562562
def (prec = Object.new).to_int; 2; end
563563
assert_equal(0.99, 0.981.ceil(prec))
564+
565+
assert_equal(10000000000, 1.0.ceil(-10), "[Bug #20654]")
566+
assert_equal(100000000000000000000, 1.0.ceil(-20), "[Bug #20654]")
567+
assert_equal(100000000000000000000000000000000000000000000000000, 1.0.ceil(-50), "[Bug #20654]")
564568
end
565569

566570
def test_truncate_with_precision

test/ruby/test_integer.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ def test_truncate
525525

526526
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.truncate(1))
527527
assert_int_equal(10**400, (10**400).truncate(1))
528+
529+
assert_int_equal(10000000000, 1.ceil(-10), "[Bug #20654]")
530+
assert_int_equal(100000000000000000000, 1.ceil(-20), "[Bug #20654]")
531+
assert_int_equal(100000000000000000000000000000000000000000000000000, 1.ceil(-50), "[Bug #20654]")
528532
end
529533

530534
MimicInteger = Struct.new(:to_int)

0 commit comments

Comments
 (0)