Ruby 1.8.7 Hash equivalence fails on large Fixnums
MacPortsのチケットに「大きなFixnumをもつHashのhash値が等しくならない」とのレポートがきてた。
% irb --simple-prompt >> h1 = {:a => 2**29} => {:a=>536870912} >> h2 = {:a => 2**29} => {:a=>536870912} >> h1.eql? h2 => true >> h1.hash == h2.hash => false
となる。
Ruby的に、
A.eql?(B) が成立する時は必ず A.hash == B.hash も成立しなければいけません。
http://www.ruby-lang.org/ja/man/html/Object.html#hash
ということなので、これは問題があるよね。
いくつか値を変えてためしてみると、Bignumになる手前あたりのFixnumで問題があるみたい。Snow LeopardのApple版の/usr/bin/rubyでも32bit/64bitとも再現した。さらに確認してみると、Hash#hashの値が呼び出しのたびに変わるぞ。
% irb --simple-prompt >> h = {:a => 2**29} => {:a=>536870912} >> h.hash => 2268924 >> h.hash => 2247252 >> h.hash => 2233296
これはMacPortsのport:rubyでなくRuby本体の問題と判断したので、Rubyのredmineのほうにエスカレーション。いちおうhash.cのrb_hash_hash()からたどってコードを読んで、原因を特定しようと試みたけども、ぜんぜん見当がつかないので状況のみを報告した。
翌日には修正されていた。しごとはええ!
修正は1.8に対してだったので、1.8.7用に調整したパッチをつくってport:rubyに追加。MacPorts用のチケットもクローズして完了。
ということでレポートくれた人、コメントくれた人、修正してくれた人、みなさんありがとうございましたっ。
それにしても最初のレポートの人はどういう状況で発見したんだろうね。Fixnumが大きいときだけとか、たどりつくまでがたいへんそうだ。