Skip to content

Commit 67b01dc

Browse files
committed
[GR-16506] Better testing of BigDecimal for future changes.
PullRequest: truffleruby/903
2 parents 5bccfc5 + ddd2cf9 commit 67b01dc

29 files changed

+273
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Compatibility:
2020
* Implemented `rb_enc_sprintf` (#1702).
2121
* Implemented `ENV#{filter,filter!}` aliases for `select` and `select!`.
2222
* Non-blocking `StringIO` and `Socket` APIs now support `exception: false` like MRI (#1702).
23+
* Increased compatibility of `BigDecimal`.
2324

2425
Changes:
2526

lib/mri/bigdecimal/util.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ class String
6868
#
6969
# TruffleRuby: MRI defines this method in C. We define it in Ruby for simplicity & clarity.
7070
def to_d
71-
begin
72-
BigDecimal(self)
73-
rescue ArgumentError
74-
BigDecimal(0)
75-
end
71+
Truffle.invoke_primitive :bigdecimal_new, self, Truffle::UNDEFINED, false
7672
end
7773
end
7874

lib/truffle/bigdecimal.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,13 @@ def frac
207207
end
208208

209209
def to_s(format = 'E')
210-
if finite?
210+
if zero?
211+
if sign == SIGN_NEGATIVE_ZERO
212+
'-0.0'
213+
else
214+
'0.0'
215+
end
216+
elsif finite?
211217
float_format = format[-1] == 'F' || format[-1] == 'f'
212218
space_frequency = format.to_i
213219
prefix = if self > 0 && [' ', '+'].include?(format[0])
@@ -321,7 +327,7 @@ def self.new(*args)
321327

322328
module Kernel
323329
def BigDecimal(value, precision = Truffle::UNDEFINED)
324-
Truffle.invoke_primitive :bigdecimal_new, value, precision
330+
Truffle.invoke_primitive :bigdecimal_new, value, precision, true
325331
end
326332
end
327333

spec/ruby/library/bigdecimal/BigDecimal_spec.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,13 @@
150150
BigDecimal("-12345.6E-1").should == -reference
151151
end
152152

153-
it 'raises ArgumentError when Float is used without precision' do
153+
it "raises ArgumentError when Float is used without precision" do
154154
lambda { BigDecimal(1.0) }.should raise_error(ArgumentError)
155155
end
156156

157+
it "returns appropriate BigDecimal zero for signed zero" do
158+
BigDecimal(-0.0, Float::DIG).sign.should == -1
159+
BigDecimal(0.0, Float::DIG).sign.should == 1
160+
end
161+
157162
end

spec/ruby/library/bigdecimal/inspect_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
@bigdec.inspect.should == "0.12345678e4"
1616
end
1717

18+
it "does not add an exponent for zero values" do
19+
BigDecimal("0").inspect.should == "0.0"
20+
BigDecimal("+0").inspect.should == "0.0"
21+
BigDecimal("-0").inspect.should == "-0.0"
22+
end
23+
1824
it "properly cases non-finite values" do
1925
BigDecimal("NaN").inspect.should == "NaN"
2026
BigDecimal("Infinity").inspect.should == "Infinity"

spec/ruby/library/bigdecimal/to_d_spec.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
require 'bigdecimal'
33
require 'bigdecimal/util'
44

5-
65
describe "Float#to_d" do
76
it "returns appropriate BigDecimal zero for signed zero" do
87
-0.0.to_d.sign.should == -1

spec/ruby/library/bigdecimal/to_s_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
@bigdec.to_s.should =~ /^0\.[0-9]*e[0-9]*$/
2020
end
2121

22+
it "does not add an exponent for zero values" do
23+
BigDecimal("0").to_s.should == "0.0"
24+
BigDecimal("+0").to_s.should == "0.0"
25+
BigDecimal("-0").to_s.should == "-0.0"
26+
end
27+
2228
it "takes an optional argument" do
2329
lambda {@bigdec.to_s("F")}.should_not raise_error()
2430
end
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
11
slow:BigDecimal is not defined unless it is required
2-
fails:Kernel#BigDecimal raises ArgumentError for invalid strings
3-
fails:Kernel#BigDecimal does not ignores trailing garbage
4-
fails:Kernel#BigDecimal process underscores as Float()

spec/tags/library/bigdecimal/gt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/library/bigdecimal/gte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/library/bigdecimal/limit_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/library/bigdecimal/lt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/library/bigdecimal/lte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
aot:BigDecimal.new treats invalid strings as 0.0
2-
fails:BigDecimal.new raises ArgumentError for invalid strings

spec/tags/library/bigdecimal/to_d_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/library/bigdecimal/to_f_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ public DynamicObject argumentError(Rope message, Node currentNode, Throwable jav
198198
return ExceptionOperations.createRubyException(context, exceptionClass, StringOperations.createString(context, message), currentNode, javaThrowable);
199199
}
200200

201+
@TruffleBoundary
202+
public DynamicObject argumentErrorInvalidBigDecimal(String string, Node currentNode) {
203+
return argumentError(StringUtils.format("invalid value for BigDecimal(): \"%s\"", string), currentNode);
204+
}
205+
201206
// FrozenError
202207

203208
@TruffleBoundary

src/main/java/org/truffleruby/language/RubyGuards.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
public abstract class RubyGuards {
1818

19+
private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0);
20+
1921
// Basic Java types
2022

2123
public static boolean isBoolean(Object value) {
@@ -74,6 +76,7 @@ public static boolean isPrimitiveClass(Class<?> clazz) {
7476
return clazz == Boolean.class || clazz == Byte.class || clazz == Short.class || clazz == Integer.class
7577
|| clazz == Long.class || clazz == Float.class || clazz == Double.class;
7678
}
79+
7780
// Ruby types
7881

7982
public static boolean isRubyBasicObject(Object object) {
@@ -313,10 +316,18 @@ public static boolean isInfinity(double value) {
313316
return Double.isInfinite(value);
314317
}
315318

319+
public static boolean isFinite(double value) {
320+
return Double.isFinite(value);
321+
}
322+
316323
public static boolean isPositive(double value) {
317324
return value >= 0;
318325
}
319326

327+
public static boolean isNegativeZero(double value) {
328+
return Double.doubleToRawLongBits(value) == NEGATIVE_ZERO_DOUBLE_BITS;
329+
}
330+
320331
// Composite
321332

322333
public static boolean isSingletonClass(DynamicObject value) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public abstract class AbstractAddNode extends BigDecimalOpNode {
2525
private final ConditionProfile aNormalProfile = ConditionProfile.createBinaryProfile();
2626

2727
protected Object add(DynamicObject a, DynamicObject b, int precision) {
28+
if (precision == 0) {
29+
precision = getLimit();
30+
}
2831
return createBigDecimal(addBigDecimal(a, b, new MathContext(precision, getRoundMode())));
2932
}
3033

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ private BigDecimal divBigDecimal(BigDecimal a, BigDecimal b, MathContext mathCon
4949
}
5050

5151
protected Object div(DynamicObject a, DynamicObject b, int precision) {
52+
if (precision == 0) {
53+
precision = getLimit();
54+
}
5255
return createBigDecimal(divBigDecimalConsideringSignum(a, b, new MathContext(precision, getRoundMode())));
5356
}
5457

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ private Object multBigDecimal(BigDecimal a, BigDecimal b, MathContext mathContex
3838
}
3939

4040
protected Object mult(DynamicObject a, DynamicObject b, int precision) {
41+
if (precision == 0) {
42+
precision = getLimit();
43+
}
4144
return createBigDecimal(multBigDecimalConsideringSignum(a, b, new MathContext(precision, getRoundMode())));
4245
}
4346

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ private BigDecimal subBigDecimal(DynamicObject a, DynamicObject b, MathContext m
3030
}
3131

3232
protected Object subNormal(DynamicObject a, DynamicObject b, int precision) {
33+
if (precision == 0) {
34+
precision = getLimit();
35+
}
3336
return createBigDecimal(subBigDecimal(a, b, new MathContext(precision, getRoundMode())));
3437
}
3538

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public BigDecimal doInt(long value, RoundingMode roundingMode) {
6868
@TruffleBoundary
6969
@Specialization
7070
public BigDecimal doDouble(double value, RoundingMode roundingMode) {
71+
assert !RubyGuards.isNegativeZero(value);
7172
return BigDecimal.valueOf(value);
7273
}
7374

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,15 @@ public static boolean isNan(DynamicObject value) {
5151
}
5252

5353
protected DynamicObject createBigDecimal(Object value) {
54-
return getCreateBigDecimal().executeCreate(value, NotProvided.INSTANCE);
54+
return createBigDecimal(value, true);
5555
}
5656

57-
protected DynamicObject createBigDecimal(Object value, int digits) {
58-
return getCreateBigDecimal().executeCreate(value, digits);
57+
protected DynamicObject createBigDecimal(Object value, boolean strict) {
58+
return getCreateBigDecimal().executeCreate(value, NotProvided.INSTANCE, strict);
59+
}
60+
61+
protected DynamicObject createBigDecimal(Object value, int digits, boolean strict) {
62+
return getCreateBigDecimal().executeCreate(value, digits, strict);
5963
}
6064

6165
protected RoundingMode getRoundMode() {
@@ -110,7 +114,7 @@ protected int getLimit() {
110114
private CreateBigDecimalNode getCreateBigDecimal() {
111115
if (createBigDecimal == null) {
112116
CompilerDirectives.transferToInterpreterAndInvalidate();
113-
createBigDecimal = insert(CreateBigDecimalNodeFactory.create(null, null));
117+
createBigDecimal = insert(CreateBigDecimalNodeFactory.create(null, null, null));
114118
}
115119

116120
return createBigDecimal;

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

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ public abstract class BigDecimalNodes {
5252
public abstract static class NewNode extends BigDecimalCoreMethodArrayArgumentsNode {
5353

5454
@Specialization
55-
public Object newBigDecimal(Object value, NotProvided digits) {
56-
return createBigDecimal(value);
55+
public Object newBigDecimal(Object value, NotProvided notProvided, boolean strict) {
56+
return createBigDecimal(value, strict);
5757
}
5858

5959
@Specialization
60-
public Object newBigDecimal(Object value, int digits) {
61-
return createBigDecimal(value, digits);
60+
public Object newBigDecimal(Object value, int digits, boolean strict) {
61+
return createBigDecimal(value, digits, strict);
6262
}
6363

6464
}
@@ -971,11 +971,27 @@ public int compare(DynamicObject a, long b) {
971971
return compareBigDecimal(a, BigDecimal.valueOf(b));
972972
}
973973

974-
@Specialization(guards = "isNormal(a)")
975-
public int compare(DynamicObject a, double b) {
974+
@Specialization(guards = {
975+
"isNormal(a)",
976+
"isFinite(b)"
977+
})
978+
public int compareFinite(DynamicObject a, double b) {
976979
return compareBigDecimal(a, valueOf(b));
977980
}
978981

982+
@Specialization(guards = {
983+
"isNormal(a)",
984+
"!isFinite(b)"
985+
})
986+
public Object compareNotFinite(DynamicObject a, double b) {
987+
if (Double.isNaN(b)) {
988+
return nil();
989+
} else {
990+
assert Double.isInfinite(b);
991+
return b < 0 ? +1 : -1;
992+
}
993+
}
994+
979995
@Specialization(guards = {
980996
"isNormal(a)",
981997
"isRubyBignum(b)"
@@ -997,11 +1013,28 @@ public Object compareSpecial(DynamicObject a, long b) {
9971013
return compareSpecial(a, createBigDecimal(BigDecimal.valueOf(b)));
9981014
}
9991015

1000-
@Specialization(guards = "!isNormal(a)")
1001-
public Object compareSpecial(DynamicObject a, double b) {
1016+
@Specialization(guards = {
1017+
"!isNormal(a)",
1018+
"isFinite(b)"
1019+
})
1020+
public Object compareSpecialFinite(DynamicObject a, double b) {
10021021
return compareSpecial(a, createBigDecimal(valueOf(b)));
10031022
}
10041023

1024+
@Specialization(guards = {
1025+
"!isNormal(a)",
1026+
"!isFinite(b)"
1027+
})
1028+
public Object compareSpecialInfinite(DynamicObject a, double b) {
1029+
final BigDecimalType type = Layouts.BIG_DECIMAL.getType(a);
1030+
1031+
if (type == BigDecimalType.NAN || Double.isNaN(b)) {
1032+
return nil();
1033+
} else {
1034+
return b < 0 ? +1 : -1;
1035+
}
1036+
}
1037+
10051038
@Specialization(guards = {
10061039
"!isNormal(a)",
10071040
"isRubyBignum(b)"
@@ -1416,7 +1449,7 @@ public double toFSpecial(DynamicObject value,
14161449
return Double.POSITIVE_INFINITY;
14171450
case NEGATIVE_ZERO:
14181451
negZeroProfile.enter();
1419-
return 0.0;
1452+
return -0.0;
14201453
case NAN:
14211454
nanProfile.enter();
14221455
return Double.NaN;

0 commit comments

Comments
 (0)