Bug #9059

Equal Time objects don't hash equal

Added by Isaac Schwabacher 6 months ago. Updated 6 months ago.

[ruby-core:58070]
Status:Closed
Priority:Normal
Assignee:-
Category:-
Target version:-
ruby -v:ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] Backport:1.9.3: UNKNOWN, 2.0.0: UNKNOWN

Description

=begin
Time objects break the promise that if (({t0.eql? t1})), then (({t0.hash == t1.hash})).
It is possible that this is related to the resolution of (()), since both issues seem to be related to round-off error.

#!/usr/bin/env ruby

require 'minitest/autorun'

describe Object do
before do
t0 = Time.new(2013, 10, 29, 12, 30, 27)
t1 = t0 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
tmidnight = Time.new(2013, 10, 29)
@x = t0 - (t0.to
r - tmidnight.tor) % 60
@y = t1 - (t1.tor - tmidnight.to_r) % 60
end

it "should hash equal to eql objects" do
if @x.respondto? :eql? and @x.respondto? :hash and @y.respondto? :hash
@x.hash.must
equal @y.hash if @x.eql? @y
end
end
end

This test also failed on ruby -v ((%ruby 1.9.2p290 (2011-07-09 revision 32553) [x8664-linux]%)).
If I had to guess, I would say that the instance variables from which (({@t0.tv
})) and (({@t1.tv_})) are computed are (({#==})) but not (({#eql?})), and that (({Time#eql?})) is (correctly, I think) using (({#==})) to compare these, but (({Time#hash})) is using their (({#hash}))es directly rather than converting them to a common type first.

=end

Associated revisions

Revision 43473
Added by Akira Tanaka 6 months ago

  • time.c (v2w): Normalize a rational value to an integer if possible. [Bug #9059] reported by Isaac Schwabacher.

History

#1 Updated by Isaac Schwabacher 6 months ago

=begin
Further testing supports the above hypothesis:

$ cat testtimehash.rb
#!/usr/bin/env ruby

require 'minitest/autorun'

describe Object do
before do
@x = Time.new(2013, 10, 29, 12, 30, 27)
@y = @x + '1/1000000000'.tor - '1/1000000000'.tor
@z = @x + '1/10000000000'.tor - '1/10000000000'.tor
@w = @x + 2(8 * 0.size) - 2(8 * 0.size)
end

describe "Time with fixnum nanoseconds" do
it "should hash equal to an eql object" do
if @x.respondto? :eql? and @x.respondto? :hash and @y.respondto? :hash
@x.hash.must
equal @y.hash if @x.eql? @y
end
end
end

describe "Time with rational nanoseconds" do
it "should hash equal to an eql object" do
if @x.respondto? :eql? and @x.respondto? :hash and @z.respondto? :hash
@x.hash.must
equal @z.hash if @x.eql? @z
end
end
end

describe "Time with bignum nanoseconds" do
it "should hash equal to an eql object" do
if @x.respondto? :eql? and @x.respondto? :hash and @w.respondto? :hash
@x.hash.must
equal @w.hash if @x.eql? @w
end
end
end
end

$ ruby testtimehash.rb
Run options: --seed 21051

# Running tests:

..F

Finished tests in 0.170915s, 17.5526 tests/s, 17.5526 assertions/s.

1) Failure:
test0001should hash equal to an eql object(Object::Time with rational nanoseconds) [testtimehash.rb:24]:
Expected: 383634095889643923
Actual: 1265190933511225464

3 tests, 3 assertions, 1 failures, 0 errors, 0 skips

I found it interesting that the Bignum test passed while the Rational test failed, so I followed up:

2.0.0-p247 :001 > (2(8 * 0.size - 2)).class # the smallest positive integer that can't be a Fixnum
=> Bignum
2.0.0-p247 :003 > (2
(8 * 0.size - 2) - 2**(8 * 0.size - 2)).class
=> Fixnum
2.0.0-p247 :004 > ('1/10'.tor - '1/10'.tor).class
=> Rational

It looks like I did not actually succeed in testing the Bignum case; I suspect that it will show the same behavior as the Rational case if you can figure out how to get a Bignum and a Fixnum that compare equal.

HTH

=end

#2 Updated by Akira Tanaka 6 months ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r43473.
Isaac, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • time.c (v2w): Normalize a rational value to an integer if possible. [Bug #9059] reported by Isaac Schwabacher.

Also available in: Atom PDF