Bug #16700

Inconsistent behavior of equal? between Integer and Float

Added by thyresias (Thierry Lambert) 12 months ago. Updated 12 months ago.

Target version:
ruby -v:
ruby 2.6.5p114 (2019-10-01 revision 67812) [i386-mingw32]


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) 12 months 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) 12 months 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 VALUEs 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) 12 months 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) 12 months 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):

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) 12 months ago

Also, immutability doesn't mean objects with the same value are the same instance.
Take integers > 264 or BigDecimal for instance, they are objects and they have their own identity.

Also available in: Atom PDF