Skip to content

Commit 93a4054

Browse files
committed
[GR-15764] Fix creating BigDecimal instances from non-finite Float values.
PullRequest: truffleruby/834
2 parents 79898d8 + 7978b16 commit 93a4054

File tree

3 files changed

+69
-6
lines changed

3 files changed

+69
-6
lines changed

CHANGELOG.md

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

55
* Fixed `BigDecimal#dup` so it now just returns the receiver, per Ruby 2.5+ semantics (#1680).
6+
* Fixed creating `BigDecimal` instances from non-finite `Float` values (#1685).
67
* Implemented `rb_eval_string_protect`.
78
* Fixed `rb_get_kwargs` to correctly handle optional and rest arguments.
89

spec/ruby/library/bigdecimal/BigDecimal_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,34 @@
100100
neg_inf.should < 0
101101
end
102102

103+
describe "accepts NaN and [+-]Infinity as Float values" do
104+
it "works without an explicit precision" do
105+
BigDecimal(Float::NAN).nan?.should == true
106+
107+
pos_inf = BigDecimal(Float::INFINITY)
108+
pos_inf.finite?.should == false
109+
pos_inf.should > 0
110+
pos_inf.should == BigDecimal("+Infinity")
111+
112+
neg_inf = BigDecimal(-Float::INFINITY)
113+
neg_inf.finite?.should == false
114+
neg_inf.should < 0
115+
end
116+
117+
it "works with an explicit precision" do
118+
BigDecimal(Float::NAN, Float::DIG).nan?.should == true
119+
120+
pos_inf = BigDecimal(Float::INFINITY, Float::DIG)
121+
pos_inf.finite?.should == false
122+
pos_inf.should > 0
123+
pos_inf.should == BigDecimal("+Infinity")
124+
125+
neg_inf = BigDecimal(-Float::INFINITY, Float::DIG)
126+
neg_inf.finite?.should == false
127+
neg_inf.should < 0
128+
end
129+
end
130+
103131
it "allows for [eEdD] as exponent separator" do
104132
reference = BigDecimal("12345.67E89")
105133

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

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.oracle.truffle.api.dsl.NodeChild;
1616
import com.oracle.truffle.api.dsl.Specialization;
1717
import com.oracle.truffle.api.object.DynamicObject;
18+
import com.oracle.truffle.api.profiles.BranchProfile;
1819
import com.oracle.truffle.api.profiles.ConditionProfile;
1920
import org.truffleruby.Layouts;
2021
import org.truffleruby.core.cast.BooleanCastNode;
@@ -68,16 +69,33 @@ public DynamicObject create(long value, int digits,
6869
}
6970

7071
@Specialization
71-
public DynamicObject create(double value, NotProvided digits) {
72-
throw new RaiseException(getContext(), coreExceptions().argumentErrorCantOmitPrecision(this));
72+
public DynamicObject create(double value, NotProvided digits,
73+
@Cached("createBinaryProfile()") ConditionProfile finiteValueProfile,
74+
@Cached("create()") BranchProfile nanProfile,
75+
@Cached("create()") BranchProfile positiveInfinityProfile,
76+
@Cached("create()") BranchProfile negativeInfinityProfile) {
77+
if (finiteValueProfile.profile(Double.isFinite(value))) {
78+
throw new RaiseException(getContext(), coreExceptions().argumentErrorCantOmitPrecision(this));
79+
} else {
80+
return createNonFiniteBigDecimal(value, nanProfile, positiveInfinityProfile, negativeInfinityProfile);
81+
}
7382
}
7483

7584
@Specialization
7685
public DynamicObject create(double value, int digits,
77-
@Cached("create()") BigDecimalCastNode bigDecimalCastNode) {
78-
final RoundingMode roundMode = getRoundMode();
79-
final BigDecimal bigDecimal = (BigDecimal) bigDecimalCastNode.execute(value, roundMode);
80-
return createNormalBigDecimal(round(bigDecimal, new MathContext(digits, roundMode)));
86+
@Cached("create()") BigDecimalCastNode bigDecimalCastNode,
87+
@Cached("createBinaryProfile()") ConditionProfile finiteValueProfile,
88+
@Cached("create()") BranchProfile nanProfile,
89+
@Cached("create()") BranchProfile positiveInfinityProfile,
90+
@Cached("create()") BranchProfile negativeInfinityProfile) {
91+
if (finiteValueProfile.profile(Double.isFinite(value))) {
92+
final RoundingMode roundMode = getRoundMode();
93+
final BigDecimal bigDecimal = (BigDecimal) bigDecimalCastNode.execute(value, roundMode);
94+
95+
return createNormalBigDecimal(round(bigDecimal, new MathContext(digits, roundMode)));
96+
} else {
97+
return createNonFiniteBigDecimal(value, nanProfile, positiveInfinityProfile, negativeInfinityProfile);
98+
}
8199
}
82100

83101
@Specialization(guards = "type == NEGATIVE_INFINITY || type == POSITIVE_INFINITY")
@@ -249,4 +267,20 @@ private Object getValueFromString(String string, int digits) {
249267
}
250268
}
251269

270+
private DynamicObject createNonFiniteBigDecimal(double value, BranchProfile nanProfile,
271+
BranchProfile positiveInfinityProfile, BranchProfile negativeInfinityProfile) {
272+
if (Double.isNaN(value)) {
273+
nanProfile.enter();
274+
return createSpecialBigDecimal(BigDecimalType.NAN);
275+
} else {
276+
if (value == Double.POSITIVE_INFINITY) {
277+
positiveInfinityProfile.enter();
278+
return createSpecialBigDecimal(BigDecimalType.POSITIVE_INFINITY);
279+
} else {
280+
negativeInfinityProfile.enter();
281+
return createSpecialBigDecimal(BigDecimalType.NEGATIVE_INFINITY);
282+
}
283+
}
284+
}
285+
252286
}

0 commit comments

Comments
 (0)