Project

General

Profile

Actions

Bug #16700

closed

Inconsistent behavior of equal? between Integer and Float

Added by thyresias (Thierry Lambert) about 4 years ago. Updated about 4 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
ruby -v:
ruby 2.6.5p114 (2019-10-01 revision 67812) [i386-mingw32]
[ruby-core:97567]

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) about 4 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) about 4 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 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) about 4 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) about 4 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) about 4 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0