Skip to content

Commit 71ce7e1

Browse files
authored
[Bug #19335] Integer#remainder should respect #coerce (ruby#7120)
Also `Numeric#remainder` should.
1 parent 1ddeb74 commit 71ce7e1

File tree

4 files changed

+44
-9
lines changed

4 files changed

+44
-9
lines changed

numeric.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ num_modulo(VALUE x, VALUE y)
740740
static VALUE
741741
num_remainder(VALUE x, VALUE y)
742742
{
743+
if (!rb_obj_is_kind_of(y, rb_cNumeric)) {
744+
do_coerce(&x, &y, TRUE);
745+
}
743746
VALUE z = num_funcall1(x, '%', y);
744747

745748
if ((!rb_equal(z, INT2FIX(0))) &&
@@ -4363,12 +4366,22 @@ static VALUE
43634366
int_remainder(VALUE x, VALUE y)
43644367
{
43654368
if (FIXNUM_P(x)) {
4366-
return num_remainder(x, y);
4369+
if (FIXNUM_P(y)) {
4370+
VALUE z = fix_mod(x, y);
4371+
assert(FIXNUM_P(z));
4372+
if (z != INT2FIX(0) && (SIGNED_VALUE)(x ^ y) < 0)
4373+
z = fix_minus(z, y);
4374+
return z;
4375+
}
4376+
else if (!RB_BIGNUM_TYPE_P(y)) {
4377+
return num_remainder(x, y);
4378+
}
4379+
x = rb_int2big(FIX2LONG(x));
43674380
}
4368-
else if (RB_BIGNUM_TYPE_P(x)) {
4369-
return rb_big_remainder(x, y);
4381+
else if (!RB_BIGNUM_TYPE_P(x)) {
4382+
return Qnil;
43704383
}
4371-
return Qnil;
4384+
return rb_big_remainder(x, y);
43724385
}
43734386

43744387
static VALUE

spec/ruby/core/numeric/remainder_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
@obj = NumericSpecs::Subclass.new
77
@result = mock("Numeric#% result")
88
@other = mock("Passed Object")
9+
ruby_version_is "3.3" do
10+
@other.should_receive(:coerce).with(@obj).and_return([@obj, @other])
11+
end
912
end
1013

1114
it "returns the result of calling self#% with other if self is 0" do

test/ruby/test_float.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ def test_remainder
227227
assert_equal(-3.5, (-11.5).remainder(-4))
228228
assert_predicate(Float::NAN.remainder(4), :nan?)
229229
assert_predicate(4.remainder(Float::NAN), :nan?)
230+
231+
ten = Object.new
232+
def ten.coerce(other)
233+
[other, 10]
234+
end
235+
assert_equal(4, 14.0.remainder(ten))
230236
end
231237

232238
def test_to_s

test/ruby/test_integer_comb.rb

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -408,19 +408,32 @@ def test_truncate
408408
end
409409

410410
def test_remainder
411+
coerce = EnvUtil.labeled_class("CoerceNum") do
412+
def initialize(num)
413+
@num = num
414+
end
415+
def coerce(other)
416+
[other, @num]
417+
end
418+
def inspect
419+
"#{self.class.name}(#@num)"
420+
end
421+
alias to_s inspect
422+
end
423+
411424
VS.each {|a|
412-
VS.each {|b|
413-
if b == 0
425+
(VS + VS.map {|b| [coerce.new(b), b]}).each {|b, i = b|
426+
if i == 0
414427
assert_raise(ZeroDivisionError) { a.divmod(b) }
415428
else
416-
r = a.remainder(b)
429+
r = assert_nothing_raised(ArgumentError, "#{a}.remainder(#{b})") {a.remainder(b)}
417430
assert_kind_of(Integer, r)
418431
if a < 0
419-
assert_operator(-b.abs, :<, r, "#{a}.remainder(#{b})")
432+
assert_operator(-i.abs, :<, r, "#{a}.remainder(#{b})")
420433
assert_operator(0, :>=, r, "#{a}.remainder(#{b})")
421434
elsif 0 < a
422435
assert_operator(0, :<=, r, "#{a}.remainder(#{b})")
423-
assert_operator(b.abs, :>, r, "#{a}.remainder(#{b})")
436+
assert_operator(i.abs, :>, r, "#{a}.remainder(#{b})")
424437
else
425438
assert_equal(0, r, "#{a}.remainder(#{b})")
426439
end

0 commit comments

Comments
 (0)