Skip to content

Commit 89e5a01

Browse files
committed
Bigdecimal coerce
PullRequest: truffleruby/675
2 parents 30170b9 + 54f22f2 commit 89e5a01

File tree

11 files changed

+138
-21
lines changed

11 files changed

+138
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Bug fixes:
44

55
* Implement `rb_io_wait_writable` (#1586).
66
* Fixed error when using arrows keys first within `irb` or `pry` (#1478, #1486).
7+
* Coerce the right hand side for all `BigDecimal` operations (#1598).
78

89
Changes:
910

spec/ruby/library/bigdecimal/add_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@
7373
# BigDecimal("0.88").add(0.0, 1).should == BigDecimal("0.9")
7474
# end
7575

76+
describe "with Object" do
77+
it "tries to coerce the other operand to self" do
78+
object = mock("Object")
79+
object.should_receive(:coerce).with(@frac_3).and_return([@frac_3, @frac_4])
80+
@frac_3.add(object, 1).should == BigDecimal("0.1E16")
81+
end
82+
end
83+
7684
it "favors the precision specified in the second argument over the global limit" do
7785
BigDecimalSpecs.with_limit(1) do
7886
BigDecimal('0.888').add(@zero, 3).should == BigDecimal('0.888')

spec/ruby/library/bigdecimal/div_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
}
4343
end
4444

45+
describe "with Object" do
46+
it "tries to coerce the other operand to self" do
47+
object = mock("Object")
48+
object.should_receive(:coerce).with(@one).and_return([@one, @two])
49+
@one.div(object).should == @zero
50+
end
51+
end
52+
4553
it "raises FloatDomainError if NaN is involved" do
4654
lambda { @one.div(@nan) }.should raise_error(FloatDomainError)
4755
lambda { @nan.div(@one) }.should raise_error(FloatDomainError)

spec/ruby/library/bigdecimal/mult_spec.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
describe "BigDecimal#mult" do
1010
before :each do
1111
@one = BigDecimal "1"
12-
@e3_minus = BigDecimal "3E-20001"
12+
@e3_minus = BigDecimal("3E-20001")
13+
@e3_plus = BigDecimal("3E20001")
1314
@e = BigDecimal "1.00000000000000000000123456789"
1415
@tolerance = @e.sub @one, 1000
1516
@tolerance2 = BigDecimal "30001E-20005"
@@ -21,4 +22,11 @@
2122
@e3_minus.mult(@one, 1).should be_close(0, @tolerance2)
2223
end
2324

25+
describe "with Object" do
26+
it "tries to coerce the other operand to self" do
27+
object = mock("Object")
28+
object.should_receive(:coerce).with(@e3_minus).and_return([@e3_minus, @e3_plus])
29+
@e3_minus.mult(object, 1).should == BigDecimal("9")
30+
end
31+
end
2432
end

spec/ruby/library/bigdecimal/multiply_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,12 @@
2323
(@e3_minus * @e3_minus).should == BigDecimal("9E-40002")
2424
(@e * @one).should == @e
2525
end
26+
27+
describe "with Object" do
28+
it "tries to coerce the other operand to self" do
29+
object = mock("Object")
30+
object.should_receive(:coerce).with(@e3_minus).and_return([@e3_minus, @e3_plus])
31+
(@e3_minus * object).should == BigDecimal("9")
32+
end
33+
end
2634
end

spec/ruby/library/bigdecimal/remainder_spec.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
before :each do
77
@zero = BigDecimal("0")
8-
@one = BigDecimal("0")
8+
@one = BigDecimal("1")
9+
@three = BigDecimal("3")
910
@mixed = BigDecimal("1.23456789")
1011
@pos_int = BigDecimal("2E5555")
1112
@neg_int = BigDecimal("-2E5555")
@@ -71,9 +72,16 @@
7172
end
7273

7374
it "coerces arguments to BigDecimal if possible" do
74-
@one.remainder(2).should == @one
75+
@three.remainder(2).should == @one
7576
end
7677

78+
describe "with Object" do
79+
it "tries to coerce the other operand to self" do
80+
object = mock("Object")
81+
object.should_receive(:coerce).with(@three).and_return([@three, 2])
82+
@three.remainder(object).should == @one
83+
end
84+
end
7785

7886
it "raises TypeError if the argument cannot be coerced to BigDecimal" do
7987
lambda {

spec/ruby/library/bigdecimal/shared/modulo.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@
7070
res.kind_of?(BigDecimal).should == true
7171
end
7272

73+
describe "with Object" do
74+
it "tries to coerce the other operand to self" do
75+
bd6543 = BigDecimal("6543.21")
76+
object = mock("Object")
77+
object.should_receive(:coerce).with(bd6543).and_return([bd6543, 137])
78+
bd6543.send(@method, object, *@object).should == BigDecimal("104.21")
79+
end
80+
end
81+
7382
it "returns NaN if NaN is involved" do
7483
@nan.send(@method, @nan).nan?.should == true
7584
@nan.send(@method, @one).nan?.should == true

spec/ruby/library/bigdecimal/shared/quo.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
@one.send(@method, BigDecimal('2E-5555'), *@object).should == BigDecimal('0.5E5555')
3030
end
3131

32+
describe "with Object" do
33+
it "tries to coerce the other operand to self" do
34+
object = mock("Object")
35+
object.should_receive(:coerce).with(@one).and_return([@one, @two])
36+
@one.send(@method, object, *@object).should == BigDecimal("0.5")
37+
end
38+
end
39+
3240
it "returns 0 if divided by Infinity" do
3341
@zero.send(@method, @infinity, *@object).should == 0
3442
@frac_2.send(@method, @infinity, *@object).should == 0

spec/ruby/library/bigdecimal/sub_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
@one_minus = BigDecimal("-1")
1414
@frac_1 = BigDecimal("1E-99999")
1515
@frac_2 = BigDecimal("0.9E-99999")
16+
@frac_3 = BigDecimal("12345E10")
17+
@frac_4 = BigDecimal("98765E10")
1618
end
1719

1820
it "returns a - b with given precision" do
@@ -32,6 +34,14 @@
3234
@frac_1.sub(@frac_1, 1000000).should == @zero
3335
end
3436

37+
describe "with Object" do
38+
it "tries to coerce the other operand to self" do
39+
object = mock("Object")
40+
object.should_receive(:coerce).with(@frac_3).and_return([@frac_3, @frac_4])
41+
@frac_3.sub(object, 1).should == BigDecimal("-0.9E15")
42+
end
43+
end
44+
3545
it "returns NaN if NaN is involved" do
3646
@one.sub(@nan, 1).nan?.should == true
3747
@nan.sub(@one, 1).nan?.should == true

src/main/java/org/truffleruby/stdlib/bigdecimal/BigDecimalNodes.java

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,7 @@ public Object addSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b) {
8484
return addSpecial(frame, a, b, 0);
8585
}
8686

87-
@Specialization(guards = {
88-
"isRubyBigDecimal(a)",
89-
"!isRubyBigDecimal(b)"
90-
})
87+
@Specialization(guards = "!isRubyBigDecimal(b)")
9188
public Object addCoerced(DynamicObject a, Object b,
9289
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
9390
return redoCoerced.call(a, "redo_coerced", coreStrings().PLUS.getSymbol(), b);
@@ -115,6 +112,12 @@ protected Object add(VirtualFrame frame, DynamicObject a, DynamicObject b, int p
115112
protected Object addSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b, int precision) {
116113
return super.addSpecial(frame, a, b, precision);
117114
}
115+
116+
@Specialization(guards = "!isRubyBigDecimal(b)")
117+
public Object addCoerced(DynamicObject a, Object b, int precision,
118+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
119+
return redoCoerced.call(a, "redo_coerced", getSymbol("add"), b, precision);
120+
}
118121
}
119122

120123
@CoreMethod(names = "-", required = 1)
@@ -136,10 +139,7 @@ public Object subSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b) {
136139
return subSpecial(frame, a, b, 0);
137140
}
138141

139-
@Specialization(guards = {
140-
"isRubyBigDecimal(a)",
141-
"!isRubyBigDecimal(b)"
142-
})
142+
@Specialization(guards = "!isRubyBigDecimal(b)")
143143
public Object subCoerced(DynamicObject a, Object b,
144144
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
145145
return redoCoerced.call(a, "redo_coerced", coreStrings().MINUS.getSymbol(), b);
@@ -167,6 +167,12 @@ public Object subNormal(VirtualFrame frame, DynamicObject a, DynamicObject b, in
167167
public Object subSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b, int precision) {
168168
return super.subSpecial(frame, a, b, precision);
169169
}
170+
171+
@Specialization(guards = "!isRubyBigDecimal(b)")
172+
public Object subCoerced(DynamicObject a, Object b, int precision,
173+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
174+
return redoCoerced.call(a, "redo_coerced", getSymbol("sub"), b, precision);
175+
}
170176
}
171177

172178
@CoreMethod(names = "-@")
@@ -252,6 +258,12 @@ public Object multSpecialNormal(VirtualFrame frame, DynamicObject a, DynamicObje
252258
public Object multSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b) {
253259
return multSpecial(frame, a, b, 0);
254260
}
261+
262+
@Specialization(guards = "!isRubyBigDecimal(b)")
263+
public Object multCoerced(DynamicObject a, Object b,
264+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
265+
return redoCoerced.call(a, "redo_coerced", coreStrings().MULTIPLY.getSymbol(), b);
266+
}
255267
}
256268

257269
@CoreMethod(names = "mult", required = 2, lowerFixnum = 2)
@@ -293,6 +305,12 @@ public Object multSpecialNormal(VirtualFrame frame, DynamicObject a, DynamicObje
293305
public Object multSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b, int precision) {
294306
return super.multSpecial(frame, a, b, precision);
295307
}
308+
309+
@Specialization(guards = "!isRubyBigDecimal(b)")
310+
public Object multCoerced(DynamicObject a, Object b, int precision,
311+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
312+
return redoCoerced.call(a, "redo_coerced", getSymbol("mult"), b, precision);
313+
}
296314
}
297315

298316
@CoreMethod(names = { "/", "quo" }, required = 1)
@@ -330,6 +348,12 @@ public Object divSpecialNormal(VirtualFrame frame, DynamicObject a, DynamicObjec
330348
public Object divSpecialSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b) {
331349
return divSpecialSpecial(frame, a, b, 0);
332350
}
351+
352+
@Specialization(guards = "!isRubyBigDecimal(b)")
353+
public Object divCoerced(DynamicObject a, Object b,
354+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
355+
return redoCoerced.call(a, "redo_coerced", coreStrings().DIVIDE.getSymbol(), b);
356+
}
333357
}
334358

335359
@CoreMethod(names = "div", required = 1, optional = 1, lowerFixnum = 2)
@@ -469,6 +493,18 @@ public Object divSpecialSpecial(
469493
public Object divSpecialSpecial(VirtualFrame frame, DynamicObject a, DynamicObject b, int precision) {
470494
return super.divSpecialSpecial(frame, a, b, precision);
471495
}
496+
497+
@Specialization(guards = "!isRubyBigDecimal(b)")
498+
public Object divCoerced(DynamicObject a, Object b, NotProvided precision,
499+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
500+
return redoCoerced.call(a, "redo_coerced", getSymbol("div"), b);
501+
}
502+
503+
@Specialization(guards = "!isRubyBigDecimal(b)")
504+
public Object divCoerced(DynamicObject a, Object b, int precision,
505+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
506+
return redoCoerced.call(a, "redo_coerced", getSymbol("div"), b, precision);
507+
}
472508
}
473509

474510
@CoreMethod(names = "divmod", required = 1)
@@ -568,6 +604,12 @@ public Object divmodSpecial(
568604
throw new UnsupportedOperationException("unreachable code branch");
569605
}
570606

607+
@Specialization(guards = "!isRubyBigDecimal(b)")
608+
public Object divmodCoerced(DynamicObject a, Object b,
609+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
610+
return redoCoerced.call(a, "redo_coerced", getSymbol("divmod"), b);
611+
}
612+
571613
}
572614

573615
@CoreMethod(names = "remainder", required = 1)
@@ -614,6 +656,12 @@ public Object remainderSpecial(
614656
return createBigDecimal(frame, BigDecimalType.NAN);
615657
}
616658
}
659+
660+
@Specialization(guards = "!isRubyBigDecimal(b)")
661+
public Object remainderCoerced(DynamicObject a, Object b,
662+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
663+
return redoCoerced.call(a, "redo_coerced", getSymbol("remainder"), b);
664+
}
617665
}
618666

619667
@CoreMethod(names = { "modulo", "%" }, required = 1)
@@ -691,6 +739,12 @@ public Object moduloSpecial(
691739

692740
throw new UnsupportedOperationException("unreachable code branch");
693741
}
742+
743+
@Specialization(guards = "!isRubyBigDecimal(b)")
744+
public Object moduloCoerced(DynamicObject a, Object b,
745+
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
746+
return redoCoerced.call(a, "redo_coerced", getSymbol("modulo"), b);
747+
}
694748
}
695749

696750
@CoreMethod(names = { "**", "power" }, required = 1, optional = 1, lowerFixnum = { 1, 2 })
@@ -1062,15 +1116,10 @@ public Object compareSpecial(DynamicObject a, DynamicObject b) {
10621116
return aCompare.compareTo(bCompare);
10631117
}
10641118

1065-
@Specialization(guards = "isNil(b)")
1066-
public Object compareNil(DynamicObject a, DynamicObject b) {
1067-
return nil();
1068-
}
1069-
1070-
@Specialization(guards = { "!isRubyBigDecimal(b)", "!isNil(b)" })
1119+
@Specialization(guards = "!isRubyBigDecimal(b)")
10711120
public Object compareCoerced(DynamicObject a, DynamicObject b,
1072-
@Cached("createPrivate()") CallDispatchHeadNode redoCoerced) {
1073-
return redoCoerced.call(a, "redo_coerced", coreStrings().SPACESHIP.getSymbol(), b);
1121+
@Cached("createPrivate()") CallDispatchHeadNode redoCompare) {
1122+
return redoCompare.call(a, "redo_compare_no_error", b);
10741123
}
10751124

10761125
@TruffleBoundary

0 commit comments

Comments
 (0)