Skip to content

Commit 293ccb2

Browse files
committed
BigDecimal#hash should return the same value for logically equivalent BigDecimal instances.
1 parent 8728a65 commit 293ccb2

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Bug fixes:
55
* Fixed `BigDecimal#{clone,dup}` so it now just returns the receiver, per Ruby 2.5+ semantics (#1680).
66
* Fixed creating `BigDecimal` instances from non-finite `Float` values (#1685).
77
* Fixed `BigDecimal#inspect` output for non-finite values (e.g, NaN or -Infinity) (#1683).
8+
* Fixed `BigDecimal#hash` to return the same value for two `BigDecimal` objects that are equal (#1656).
89
* Added missing `BigDecimal` constant definitions (#1684).
910
* Implemented `rb_eval_string_protect`.
1011
* Fixed `rb_get_kwargs` to correctly handle optional and rest arguments.

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,4 +1545,30 @@ public int toISpecial(DynamicObject value) {
15451545
}
15461546
}
15471547

1548+
@CoreMethod(names = "hash")
1549+
public abstract static class HashNode extends BigDecimalCoreMethodArrayArgumentsNode {
1550+
1551+
private static final int CLASS_SALT = 1468180038; // random number, stops hashes for similar values but different classes being the same, static because we want deterministic hashes.
1552+
1553+
@TruffleBoundary
1554+
@Specialization(guards = "isNormal(value)")
1555+
public Object hashNormal(DynamicObject value) {
1556+
// Ruby treats trailing zeroes as insignificant for hash calculation. Java's BigDecimal, however,
1557+
// may return different hash values for two numerically equivalent values with a different number
1558+
// of trailing zeroes. Stripping them away avoids the issue.
1559+
final BigDecimal bigDecimalValue = Layouts.BIG_DECIMAL.getValue(value).stripTrailingZeros();
1560+
1561+
return getContext().getHashing(this).hash(CLASS_SALT, bigDecimalValue.hashCode());
1562+
}
1563+
1564+
@TruffleBoundary
1565+
@Specialization(guards = "!isNormal(value)")
1566+
public Object hashSpecial(DynamicObject value) {
1567+
final BigDecimalType type = Layouts.BIG_DECIMAL.getType(value);
1568+
1569+
return getContext().getHashing(this).hash(CLASS_SALT, System.identityHashCode(type));
1570+
}
1571+
1572+
}
1573+
15481574
}

0 commit comments

Comments
 (0)