きりかノート 3冊め

おあそびプログラミング

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 LeopardApple版の/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本体の問題と判断したので、Rubyredmineのほうにエスカレーション。いちおうhash.cのrb_hash_hash()からたどってコードを読んで、原因を特定しようと試みたけども、ぜんぜん見当がつかないので状況のみを報告した。

翌日には修正されていた。しごとはええ!

修正は1.8に対してだったので、1.8.7用に調整したパッチをつくってport:rubyに追加。MacPorts用のチケットもクローズして完了。

ということでレポートくれた人、コメントくれた人、修正してくれた人、みなさんありがとうございましたっ。

それにしても最初のレポートの人はどういう状況で発見したんだろうね。Fixnumが大きいときだけとか、たどりつくまでがたいへんそうだ。