From: "zakhar (Isaac Schwabacher)" Date: 2013-10-30T07:09:40+09:00 Subject: [ruby-core:58071] [ruby-trunk - Bug #9059] Equal Time objects don't hash equal Issue #9059 has been updated by zakhar (Isaac Schwabacher). =begin Further testing supports the above hypothesis: $ cat test_time_hash.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/1_000_000_000'.to_r - '1/1_000_000_000'.to_r @z = @x + '1/10_000_000_000'.to_r - '1/10_000_000_000'.to_r @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.respond_to? :eql? and @x.respond_to? :hash and @y.respond_to? :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.respond_to? :eql? and @x.respond_to? :hash and @z.respond_to? :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.respond_to? :eql? and @x.respond_to? :hash and @w.respond_to? :hash @x.hash.must_equal @w.hash if @x.eql? @w end end end end $ ruby test_time_hash.rb Run options: --seed 21051 # Running tests: ..F Finished tests in 0.170915s, 17.5526 tests/s, 17.5526 assertions/s. 1) Failure: test_0001_should hash equal to an eql object(Object::Time with rational nanoseconds) [test_time_hash.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'.to_r - '1/10'.to_r).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 ---------------------------------------- Bug #9059: Equal Time objects don't hash equal https://bugs.ruby-lang.org/issues/9059#change-42650 Author: zakhar (Isaac Schwabacher) Status: Open 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 =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 t_midnight = Time.new(2013, 10, 29) @x = t0 - (t0.to_r - t_midnight.to_r) % 60 @y = t1 - (t1.to_r - t_midnight.to_r) % 60 end it "should hash equal to eql objects" do if @x.respond_to? :eql? and @x.respond_to? :hash and @y.respond_to? :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) [x86_64-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 -- http://bugs.ruby-lang.org/