Bug #16700
closedInconsistent behavior of equal? between Integer and Float
Description
0.equal?(0) #=> true
0.0.equal?(0.0) #=> false
x = 0.0
x.equal?(x) #=> true
Since Float objects are immutable, I would expect 0.0.equal?(0.0) to be true.
Updated by sawa (Tsuyoshi Sawada) almost 5 years ago
The following versions:
- ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
- ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
- ruby 2.8.0dev (2020-03-18T04:57:03Z master afd23ed049) [x86_64-linux]
all give this result:
0.0.equal?(0.0) # => true
Win specific issue?
Updated by jeremyevans0 (Jeremy Evans) almost 5 years ago
- Status changed from Open to Rejected
On 64-bit in most common build environments, most commonly used Float
values are embedded in the VALUE
, instead of the VALUE
being a pointer to an object slot. This is called Flonum
internally. equal?
checks if the VALUE
for the object is the same, so if two VALUE
s are the same, it returns true.
@thyresias (Thierry Lambert) is using 32-bit and @sawa (Tsuyoshi Sawada) is using 64-bit, that is causing the difference in behavior. Note that behavior depends on the size of the number even in cases Flonum
is supported, because only a certain range of values can be embedded in the VALUE
. Example:
0.equal?(0) # => true
0.0.equal?(0.0) # => true
(2**64).equal?(2**64) # => false
(1e303).equal?(1e303) # => false
In short, this isn't a bug, it is expected behavior.
Updated by thyresias (Thierry Lambert) almost 5 years ago
I am a bit surprised by @jeremyevans0 (Jeremy Evans) statement:
In short, this isn't a bug, it is expected behavior.
Regardless of implementation, I think the behavior I expect is the right one: if, in binary representation, two Float numbers are identical, I would expect them to be the same object (because they are immutable). Am I missing something?
Updated by Eregon (Benoit Daloze) almost 5 years ago
It's a side effect of MRI using C's ==
to implement equal?
.
In TruffleRuby reference equality has its own logic, so the results are more consistent (e.g., all Floats are compared by their binary representation):
https://github.com/oracle/truffleruby/blob/74a40b57ffaafbc26fe1b9d3cf22cf21ad7191bf/src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java#L89-L169
I guess MRI could do the same, but it would likely occur some performance hit.
In any case, comparing numbers by identity is usually an anti-pattern, and I think that's why MRI can be a bit loose on e.g. #equal? with Floats here.
Updated by Eregon (Benoit Daloze) almost 5 years ago
Also, immutability doesn't mean objects with the same value are the same instance.
Take integers > 2^64 or BigDecimal for instance, they are objects and they have their own identity.