From: Tadashi Saito Date: 2009-10-14T11:37:22+09:00 Subject: [ruby-dev:39482] Re: BigDecimal#to_i/to_int の挙動について 斎藤と申します。 On Tue, 13 Oct 2009 16:43:24 +0900 KAKUTANI Shintaro wrote: > rubyspec を1.8ターゲットで動かしていて、標記のspecが手元では通らないことに気づきました。 > 何が正しいのか自分ではわからないので教えてもらえるとうれしいです。 僕もちゃんとは分かっていないのですが、参考になりそうな情報を並べてみます。 > BigDecimalで InfinityとNaN を to_i/to_int すると、 > 手元の環境では次のようになりました: > (動かしているバージョンが少し前のもので恐縮です) > > $ /usr/bin/ruby -v -rbigdecimal -e '%w[Infinity NaN].each{|s| v = BigDecimal.new(s); i = v.to_i rescue $!.class; print "#{v}:";p i}' > ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0] > Infinity:nil > NaN:nil > > $ ruby -v -rbigdecimal -e '%w[Infinity NaN].each{|s| v = BigDecimal.new(s); i = v.to_i rescue $!.class; print "#{v}:";p i}' > ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9] > Infinity:FloatDomainError > NaN:FloatDomainError まず1.8に関してですが、1.8.7でコミットミスが起きて、意図しない仕様変更になってしまって いるような気がします。 1.8.6も1.8.7も、最初のリリース時(pl0)は、例外が起こりませんでした。ソースを遡ると、 例外が起きるようになったのは1.8.7-pl173からのようです。160ではnilが返るはずです。 1.8.7ブランチを見ると、r23645で例外が上がるようになっています。この部分のログを見ると、 r23645 | shyouhei | 2009-06-08 08:37:55 +0900 (Mon, 08 Jun 2009) | 10 lines merge revision(s) 23610:23613: * ext/bigdecimal/bigdecimal.c (VpAlloc): avoid ALLOCA_N() to avoid segmentation fault caused by (insanely) long decimal values. backported from 1.9. * ext/bigdecimal/bigdecimal.c (BigDecimal_dump, BigDecimal_to_i, BigDecimal_to_f, BigDecimal_to_s, BigDecimal_split, BigDecimal_inspect): ditto. * ext/bigdecimal/bigdecimal.c (BigDecimal_to_f): returns Inf if exp is bigger than DBL_MANT_DIG. となっていて、例外を挙げるよう仕様変更する旨の記述は一切見られません。 そしてさらにマージ元と思しき1.8ブランチのログを見ると、r23613で例外が上がるように 仕様変更しているのに、まつもとさんがその旨をログに残していない事もわかります。 r23613 | matz | 2009-06-01 09:14:25 +0900 (月, 01 6月 2009) | 6 lines * ext/bigdecimal/bigdecimal.c (VpAlloc): avoid ALLOCA_N() to avoid segmentation fault caused by (insanely) long decimal values. backported from 1.9. * ext/bigdecimal/bigdecimal.c (BigDecimal_dump, BigDecimal_to_i, BigDecimal_to_f, BigDecimal_to_s, BigDecimal_split, BigDecimal_inspect): ditto. なので、私の推理として 1. 卜部さんは元々、1.8ブランチのバグフィクス部分だけを1.8.7ブランチにマージしたかった 2. まつもとさんが書いた1.8ブランチのログにはバグフィクスに関する記述しかなかった(不幸1) ので、卜部さんはそれを信じてしまった。 3. 卜部さんもログは確認したが、ログと無関係な変更が実際のコードにあり、仕様変更されて いる事に気づくまでには至らなかった(不幸2)。 4. 結果、バグフィクスのみのマージのつもりが、1.8.7中の仕様変更になってしまった。 5. そのまま次の1.8.7パッチレベルリリース という流れを想像するのですが、どうでしょうか > 卜部さん あるいは推理Bとしては 3'. 卜部さんはログに載っていない仕様変更を確認したが、もともとの仕様が「バグである」 と判断したためマージした。しかしログに載せないのはそのままだった。 という高度な物もありえますが、可能性は低い気がします。 # とりあえず、コミットログとチェックインの内容はちゃんと対応させてください > まつもとさん > $ ruby19 -v -rbigdecimal -e '%w[Infinity NaN].each{|s| v = BigDecimal.new(s); i = v.to_i rescue $!.class; print "#{v}:";p i}' > ruby 1.9.2dev (2009-10-05 trunk 25235) [i386-darwin9.8.0] > Infinity:FloatDomainError > NaN:FloatDomainError 1.9に関しては、 まつもとさんが[ruby-dev:37195]でこれを「あまり良くない設計」と判断 されたため、例外が発生するようになったようです。trunkのr20359です。 これは1.9.1の前のtrunkなので、1.8から1.9は明示的に仕様変更された、という話だと思って います。 以上、参考になれば幸いです。 -- 斎藤ただし