Skip to content

Commit 0922afa

Browse files
authored
[Bug #20654] Fix floor and ceil when ndigits is large (ruby#11277)
* 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. * 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 ce565cd commit 0922afa

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

numeric.c

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,11 +2373,7 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
23732373
static VALUE
23742374
rb_int_floor(VALUE num, int ndigits)
23752375
{
2376-
VALUE f;
2377-
2378-
if (int_round_zero_p(num, ndigits))
2379-
return INT2FIX(0);
2380-
f = int_pow(10, -ndigits);
2376+
VALUE f = int_pow(10, -ndigits);
23812377
if (FIXNUM_P(num) && FIXNUM_P(f)) {
23822378
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
23832379
int neg = x < 0;
@@ -2386,21 +2382,19 @@ rb_int_floor(VALUE num, int ndigits)
23862382
if (neg) x = -x;
23872383
return LONG2NUM(x);
23882384
}
2389-
if (RB_FLOAT_TYPE_P(f)) {
2390-
/* then int_pow overflow */
2391-
return INT2FIX(0);
2385+
else {
2386+
bool neg = int_neg_p(num);
2387+
if (neg) num = rb_int_minus(rb_int_plus(rb_int_uminus(num), f), INT2FIX(1));
2388+
num = rb_int_mul(rb_int_div(num, f), f);
2389+
if (neg) num = rb_int_uminus(num);
2390+
return num;
23922391
}
2393-
return rb_int_minus(num, rb_int_modulo(num, f));
23942392
}
23952393

23962394
static VALUE
23972395
rb_int_ceil(VALUE num, int ndigits)
23982396
{
2399-
VALUE f;
2400-
2401-
if (int_round_zero_p(num, ndigits))
2402-
return INT2FIX(0);
2403-
f = int_pow(10, -ndigits);
2397+
VALUE f = int_pow(10, -ndigits);
24042398
if (FIXNUM_P(num) && FIXNUM_P(f)) {
24052399
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
24062400
int neg = x < 0;
@@ -2410,11 +2404,16 @@ rb_int_ceil(VALUE num, int ndigits)
24102404
if (neg) x = -x;
24112405
return LONG2NUM(x);
24122406
}
2413-
if (RB_FLOAT_TYPE_P(f)) {
2414-
/* then int_pow overflow */
2415-
return INT2FIX(0);
2407+
else {
2408+
bool neg = int_neg_p(num);
2409+
if (neg)
2410+
num = rb_int_uminus(num);
2411+
else
2412+
num = rb_int_plus(num, rb_int_minus(f, INT2FIX(1)));
2413+
num = rb_int_mul(rb_int_div(num, f), f);
2414+
if (neg) num = rb_int_uminus(num);
2415+
return num;
24162416
}
2417-
return rb_int_plus(num, rb_int_minus(f, rb_int_modulo(num, f)));
24182417
}
24192418

24202419
VALUE

test/ruby/test_float.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,10 @@ def test_floor_with_precision
530530
assert_raise(TypeError) {1.0.floor(nil)}
531531
def (prec = Object.new).to_int; 2; end
532532
assert_equal(0.99, 0.998.floor(prec))
533+
534+
assert_equal(-10000000000, -1.0.floor(-10), "[Bug #20654]")
535+
assert_equal(-100000000000000000000, -1.0.floor(-20), "[Bug #20654]")
536+
assert_equal(-100000000000000000000000000000000000000000000000000, -1.0.floor(-50), "[Bug #20654]")
533537
end
534538

535539
def test_ceil_with_precision
@@ -557,6 +561,10 @@ def test_ceil_with_precision
557561
assert_raise(TypeError) {1.0.ceil(nil)}
558562
def (prec = Object.new).to_int; 2; end
559563
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]")
560568
end
561569

562570
def test_truncate_with_precision

test/ruby/test_integer.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ def test_floor
465465

466466
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.floor(1))
467467
assert_int_equal(10**400, (10**400).floor(1))
468+
469+
assert_int_equal(-10000000000, -1.floor(-10), "[Bug #20654]")
470+
assert_int_equal(-100000000000000000000, -1.floor(-20), "[Bug #20654]")
471+
assert_int_equal(-100000000000000000000000000000000000000000000000000, -1.floor(-50), "[Bug #20654]")
468472
end
469473

470474
def test_ceil
@@ -493,6 +497,10 @@ def test_ceil
493497

494498
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.ceil(1))
495499
assert_int_equal(10**400, (10**400).ceil(1))
500+
501+
assert_int_equal(10000000000, 1.ceil(-10), "[Bug #20654]")
502+
assert_int_equal(100000000000000000000, 1.ceil(-20), "[Bug #20654]")
503+
assert_int_equal(100000000000000000000000000000000000000000000000000, 1.ceil(-50), "[Bug #20654]")
496504
end
497505

498506
def test_truncate

0 commit comments

Comments
 (0)