|
ワナベと申します。
[ruby-core:17472] [Ruby 1.8 - Bug #206] に対するパッチを書きました。 手元では一通りテストしたつもりですが、これでいいのかいまいち確信がもてません。 どなたか検証していただけないでしょうか。 Index: ext/bigdecimal/bigdecimal.c =================================================================== --- ext/bigdecimal/bigdecimal.c (revision 17888) +++ ext/bigdecimal/bigdecimal.c (working copy) @@ -973,10 +973,8 @@ return (VALUE)0; } - mx = a->Prec; - if(mx<b->Prec) mx = b->Prec; - mx =(mx + 1) * VpBaseFig(); - GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + mx =(a->MaxPrec + b->MaxPrec + 1) * VpBaseFig(); + GUARD_OBJ(c,VpCreateRbObject(mx, "#0")); GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); VpDivd(c, res, a, b); mx = c->Prec *(VpBaseFig() + 1); -- ワナベ |
|
なかだです。
At Sat, 5 Jul 2008 22:51:48 +0900, wanabe wrote in [ruby-dev:35372]: > [ruby-core:17472] [Ruby 1.8 - Bug #206] に対するパッチを書きました。 > 手元では一通りテストしたつもりですが、これでいいのかいまいち確信がもてません。 > どなたか検証していただけないでしょうか。 BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 一方で、なぜかBigDecimal#/では有効数字が増えたりするので、使い分 けろということなんでしょうか。いずれにせよ、小林さんでないと判断 はつけられないと思います。 -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦 |
|
ワナベです。
2008/07/06 15:36 Nobuyoshi Nakada <[hidden email]>: > BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に > なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 > 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 ありがとうございます。なるほど。 BigDecimal('1').divmod(BigDecimal('3E-9')) #=> [0.3E9, 0.1E0] 上のように余りが割る数を上回るのが理解できず バグかと思ったのですが、そういうことでしたら納得しました。 -- ワナベ |
|
遠藤です。
2008年7月7日18:23 wanabe <[hidden email]>: > 2008/07/06 15:36 Nobuyoshi Nakada <[hidden email]>: >> BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に >> なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 >> 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 > > ありがとうございます。なるほど。 > > BigDecimal('1').divmod(BigDecimal('3E-9')) #=> [0.3E9, 0.1E0] > > 上のように余りが割る数を上回るのが理解できず > バグかと思ったのですが、そういうことでしたら納得しました。 これはやはりバグだと思います。 こんなこと本当は言いたくないのですが、本当に残念なことに bigdecimal は Java の BigDecimal に基づいた実装になってしまっています。 そして Java の BigDecimal は、常識的な「有効数字」とは異なる思想に 基づいているようです (加減算などが「有効数字」を勝手に増やすのも仕様) 。 http://java.sun.com/javase/ja/6/docs/ja/api/java/math/BigDecimal.html 一刻も早く bigdecimal を投げ捨てたい。 そして、その Java においても divideAndRemainder は 333.. を返します。 scala> new BigDecimal("1") divideAndRemainder new BigDecimal("3E-9") res0: Array[java.math.BigDecimal] = Array(333333333, 1E-9) よってこのパッチはあてるべきです。rubyspec のエラーも消えます。 ワナベさん、お願いしていいでしょうか。 -- Yusuke ENDOH <[hidden email]> |
|
むらたです。
On 2010/02/07, at 16:06, Yusuke ENDOH wrote: > 遠藤です。 > > 2008年7月7日18:23 wanabe <[hidden email]>: >> 2008/07/06 15:36 Nobuyoshi Nakada <[hidden email]>: >>> BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に >>> なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 >>> 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 >> >> ありがとうございます。なるほど。 >> >> BigDecimal('1').divmod(BigDecimal('3E-9')) #=> [0.3E9, 0.1E0] >> >> 上のように余りが割る数を上回るのが理解できず >> バグかと思ったのですが、そういうことでしたら納得しました。 > > > これはやはりバグだと思います。 わたしもバグだと思います。 BigDecimal#divmod の挙動は Float#divmod に合わせて、 - 商は整数 - 除数 > 0 ⇒ 0 <= 剰余 < 除数 - 除数 < 0 ⇒ 除数 < 剰余 <= 0 と決めた方が良いと思います。 -- Kenta Murata OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC 本を書きました!! 『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22 E-mail: [hidden email] twitter: http://twitter.com/mrkn/ blog: http://d.hatena.ne.jp/mrkn/ |
|
In reply to this post by Yusuke ENDOH
遠藤です。
2010年2月7日16:06 Yusuke ENDOH <[hidden email]>: > 2008年7月7日18:23 wanabe <[hidden email]>: >> 2008/07/06 15:36 Nobuyoshi Nakada <[hidden email]>: >>> BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に >>> なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 >>> 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 >> >> ありがとうございます。なるほど。 >> >> BigDecimal('1').divmod(BigDecimal('3E-9')) #=> [0.3E9, 0.1E0] >> >> 上のように余りが割る数を上回るのが理解できず >> バグかと思ったのですが、そういうことでしたら納得しました。 > > > これはやはりバグだと思います。 > > > よってこのパッチはあてるべきです。rubyspec のエラーも消えます。 すみません、rubyspec については嘘でした。 手元で試行錯誤していて、以下のようなパッチにして、rubyspec 側も / でなく div を使うように修正した上で、やっと通っています。が、 ワナベさんのパッチから精度を勝手に 2 倍にあげているので、たぶん ダメなパッチです。 mrkn さんがちゃんと考えて直してくれると信じています。 diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 8e85fdf..661b238 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -970,10 +970,8 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) return Qtrue; } - mx = a->Prec; - if(mx<b->Prec) mx = b->Prec; - mx =(mx + 1) * VpBaseFig(); - GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + mx = (a->MaxPrec + b->MaxPrec + 1) * 2 * VpBaseFig(); + GUARD_OBJ(c,VpCreateRbObject(mx, "#0")); GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); VpDivd(c, res, a, b); mx = c->Prec *(VpBaseFig() + 1); @@ -983,6 +981,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) VpAddSub(c,a,res,-1); if(!VpIsZero(c) && (VpGetSign(a)*VpGetSign(b)<0)) { VpAddSub(res,d,VpOne(),-1); + GUARD_OBJ(d,VpCreateRbObject(GetAddSubPrec(c, b)*(VpBaseFig() + 1), "0")); VpAddSub(d ,c,b, 1); *div = res; *mod = d; diff --git a/library/bigdecimal/divmod_spec.rb b/library/bigdecimal/divmod_spec.rb index 5ea6d82..d3a47fc 100644 --- a/library/bigdecimal/divmod_spec.rb +++ b/library/bigdecimal/divmod_spec.rb @@ -143,7 +143,7 @@ describe "BigDecimal#divmod" do values.each do |val2| res = val1.divmod(val2) DivmodSpecs::check_both_bigdecimal(res) - res[0].should == ((val1/val2).floor) + res[0].should == val1.div(val2) res[1].should == (val1 - res[0] * val2) end end -- Yusuke ENDOH <[hidden email]> |
|
In reply to this post by Kenta Murata
豊福です。
> - 除数 > 0 ⇒ 0 <= 剰余 < 除数 私もこの前 mod に「剰余 < 除数」を期待すると書きましたが改めて 考えたら、例えば (有効桁数1桁で1億).mod((有効桁数いくつであろうが)3) には意味のある値として何を期待すべきでしょうか。 x = BigDecimal("1E8"); sin(x,100) は sin(正確な1億,100) として 計算するべきでしょうか。それとも x は求める有効桁数の sin の計算 には有効桁数が足りないので例外にするべきでしょうか。 ともかく BigDecimal の mod, divMod に関してはまず意味を決める べきだと思います。Int, Float などとの一貫性を考えると mod, divMod というメソッドは定義すべきでないのかもしれません。 --- |
|
村田です。
On 2010/02/07, at 22:04, TOYOFUKU Chikanobu wrote: > 豊福です。 > >> - 除数 > 0 ⇒ 0 <= 剰余 < 除数 > > 私もこの前 mod に「剰余 < 除数」を期待すると書きましたが改めて > 考えたら、例えば > (有効桁数1桁で1億).mod((有効桁数いくつであろうが)3) > には意味のある値として何を期待すべきでしょうか。 > x = BigDecimal("1E8"); sin(x,100) は sin(正確な1億,100) として > 計算するべきでしょうか。それとも x は求める有効桁数の sin の計算 > には有効桁数が足りないので例外にするべきでしょうか。 一貫性を持たせたいところです。 > ともかく BigDecimal の mod, divMod に関してはまず意味を決める > べきだと思います。Int, Float などとの一貫性を考えると mod, divMod > というメソッドは定義すべきでないのかもしれません。 1.9 には Integer、Rational という二つの正確な数が組み込みで用意されています。 正確な数の表現はこれらに譲って、すべての BigDecimal は有限の精度を持つ ことにしてしまって問題はあるでしょうか? もし問題がないようなら、上記の例題は Mathematica と同じにしたいです。 Mathematica では次のようになります。 In[1]:= N[Sin[N[100000000, 1]], 100] Out[1]= 0. In[2]:= Mod[N[100000000, 1], 3] Out[2]= 0.*10^7 -- Kenta Murata OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC 本を書きました!! 『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22 E-mail: [hidden email] twitter: http://twitter.com/mrkn/ blog: http://d.hatena.ne.jp/mrkn/ |
|
In reply to this post by Yusuke ENDOH
ワナベと申します。
10/02/07 Yusuke ENDOH <[hidden email]>: > 2008年7月7日18:23 wanabe <[hidden email]>: >> 2008/07/06 15:36 Nobuyoshi Nakada <[hidden email]>: >>> BigDecimal('1').div(BigDecimal('3E-9')) の結果の有効数字が一桁に >>> なるというのは、もしかして仕様どおりの動作なんじゃないでしょうか。 >>> 左辺も右辺も、(あるいはレシーバも引数も)有効数字一桁ですから。 >> >> ありがとうございます。なるほど。 >> >> BigDecimal('1').divmod(BigDecimal('3E-9')) #=> [0.3E9, 0.1E0] >> >> 上のように余りが割る数を上回るのが理解できず >> バグかと思ったのですが、そういうことでしたら納得しました。 > > > これはやはりバグだと思います。 > よってこのパッチはあてるべきです。rubyspec のエラーも消えます。 > > ワナベさん、お願いしていいでしょうか。 大変申し訳ありません。 情けない話なのですが、パッチの意味を覚えておらず判断できかねます。 さらに議論を見ると、疑問の余地のないバグではなく意味論・仕様に踏み込むようですので メンテナまたはまつもとさんの決定が必要ではないかと思います。 ですので、まつもとさんか、あるいはメンテナとして承認された後であれば村田さんが コミットせよというのであればそうしますし、修正が必要であればそれはよろしくお願いします。 もし何かの事情で緊急性がある、とりあえずでいいからコミットしてほしい、ということであれば 問題があればすぐに revert する前提でコミットさせていただきます。 -- ワナベ |
|
In reply to this post by Kenta Murata
豊福です。
> Mathematica では次のようになります。 > In[1]:= N[Sin[N[100000000, 1]], 100] > Out[1]= 0. これはどういう計算結果による 0. でしょうか。 計算できないという意味の 0.? Sin でなく Exp だとどういう結果を返すのでしょう。 > In[2]:= Mod[N[100000000, 1], 3] > Out[2]= 0.*10^7 こういう mod は必要だと思いますしこれを divMod の余りとするのも ありそうに思いますがこれを % にした方がよいでしょうか。それとも しない方がよいでしょうか。 --- |
|
むらたです。
On 2010/02/08, at 19:36, TOYOFUKU Chikanobu wrote: > 豊福です。 > >> Mathematica では次のようになります。 >> In[1]:= N[Sin[N[100000000, 1]], 100] >> Out[1]= 0. > > これはどういう計算結果による 0. でしょうか。 > 計算できないという意味の 0.? そうではなく、有効桁数1で計算すると 0. になるという、 そのままの意味だと思います。 そもそも Sin の外側の N は不要でした In[15]:= Sin[N[100000000, 1]] Out[15]= 0. Sin のアルゴリズムによってはこうならないように できるのかもしれません。 > Sin でなく Exp だとどういう結果を返すのでしょう。 In[17]:= Exp[N[100000000, 1]] Out[17]= 0.*10^43429455 >> In[2]:= Mod[N[100000000, 1], 3] >> Out[2]= 0.*10^7 > > こういう mod は必要だと思いますしこれを divMod の余りとするのも > ありそうに思いますがこれを % にした方がよいでしょうか。それとも > しない方がよいでしょうか。 % 演算子に対応させるかどうかについては、とても悩みどころですね。 一貫して % は mod の別名としてしまうのもアリだと思います。 そうしないと定める事も間違いではないと思いますが、 その場合どのような演算になるか改めて定義が必要ですね。 -- Kenta Murata OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC 本を書きました!! 『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22 E-mail: [hidden email] twitter: http://twitter.com/mrkn/ blog: http://d.hatena.ne.jp/mrkn/ |
|
In reply to this post by TOYOFUKU Chikanobu
2010年2月7日22:04 TOYOFUKU Chikanobu <[hidden email]>:
>> - 除数 > 0 ⇒ 0 <= 剰余 < 除数 > > ?私もこの前 mod に「剰余 < 除数」を期待すると書きましたが改めて > 考えたら、例えば > (有効桁数1桁で1億).mod((有効桁数いくつであろうが)3) > には意味のある値として何を期待すべきでしょうか。 bigdecimal の元ネタっぽい Java の BigDecimal で試してみました。 precision 1 の 1E50 と、precision 1 の 3 の divideAndRemainder は precision *50* の 3333...333 と、precision 1 の 1 を返すようです。 precision っていわゆる「有効数字」じゃないみたいなんですよね。 よくわからない。 v1: 1E+50(1) v2: 3(1) v1/v2: 33333333333333333333333333333333333333333333333333(50) v1%v2: 1(1) import java.math.BigDecimal object T { def p(s: String, v: BigDecimal) { println(s + v + "(" + v.precision() + ")") } def main(args: Array[String]) { val v1 = new BigDecimal("1E50") val v2 = new BigDecimal("3") val a = v1 divideAndRemainder v2 p("v1: ", v1) p("v2: ", v2) p("v1/v2: ", a(0)) p("v1%v2: ", a(1)) } } -- Yusuke ENDOH <[hidden email]> |
|
むらたです。
On 2010/02/08, at 23:55, Yusuke ENDOH wrote: > 2010年2月7日22:04 TOYOFUKU Chikanobu <[hidden email]>: >>> - 除数 > 0 ⇒ 0 <= 剰余 < 除数 >> >> ?私もこの前 mod に「剰余 < 除数」を期待すると書きましたが改めて >> 考えたら、例えば >> (有効桁数1桁で1億).mod((有効桁数いくつであろうが)3) >> には意味のある値として何を期待すべきでしょうか。 > > > bigdecimal の元ネタっぽい Java の BigDecimal で試してみました。 > > precision 1 の 1E50 と、precision 1 の 3 の divideAndRemainder は > precision *50* の 3333...333 と、precision 1 の 1 を返すようです。 > precision っていわゆる「有効数字」じゃないみたいなんですよね。 > よくわからない。 http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/math/BigDecimal.html より ""BigDecimal クラスは、ユーザが丸め動作を完全に制御できるようにします。丸めモードが指定されず、正確な結果が表現できない場合、例外がスローされます。"" ""すべての算術演算子では、演算は、まず正確な中間結果を計算し、次に選択された丸めモードを使用して (必要な場合は) 精度設定で指定された桁数に丸めるという手順で実行されます。"" ということだそうなので、正確に計算された結果が丸められずに返ってきたんでしょうかね。 -- Kenta Murata OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC 本を書きました!! 『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22 E-mail: [hidden email] twitter: http://twitter.com/mrkn/ blog: http://d.hatena.ne.jp/mrkn/ |
|
In reply to this post by Kenta Murata
むらたです。
irc などで議論させて頂いて、大変参考になりました。 BigDecimal には有効桁数が大切な分野とそうでない分野の二種類の用途があります。 前者の場合、有効桁数が自分の計算精度を知っていてくれたほうが有り難いと思います。 かといって、Mathematica のように演算結果を有効桁数で切ってしまうと、 BigDecimal をただの多倍長浮動小数点数として使っている人には使えない代物になってしまいます。 どうしたらよいか考えてみたのですが、演算結果は常にできるだけ正確な値を算出するようにし、 オペランドから継承された有効桁数は属性として保持させ、有効桁数で切りたい人は BigDecimal#rounnd などで明示的に丸めてもらいましょうか。 BigDecimal は必ず有限の有効桁数を持つことにして、 無限精度の値は Integer と Rational で表現させるようにします。 BigDecimal を組み込み数値クラスと一緒に使えるように 若干調整が必要だと思いますが、Rational が組み込みになった 今の Ruby ではこの方法が良いのではないかと考えました。 On 2010/02/08, at 23:51, Kenta Murata wrote: > むらたです。 > > On 2010/02/08, at 19:36, TOYOFUKU Chikanobu wrote: > >> 豊福です。 >> >>> Mathematica では次のようになります。 >>> In[1]:= N[Sin[N[100000000, 1]], 100] >>> Out[1]= 0. >> >> これはどういう計算結果による 0. でしょうか。 >> 計算できないという意味の 0.? > > そうではなく、有効桁数1で計算すると 0. になるという、 > そのままの意味だと思います。 > そもそも Sin の外側の N は不要でした > > In[15]:= Sin[N[100000000, 1]] > Out[15]= 0. > > Sin のアルゴリズムによってはこうならないように > できるのかもしれません。 > >> Sin でなく Exp だとどういう結果を返すのでしょう。 > > In[17]:= Exp[N[100000000, 1]] > Out[17]= 0.*10^43429455 > >>> In[2]:= Mod[N[100000000, 1], 3] >>> Out[2]= 0.*10^7 >> >> こういう mod は必要だと思いますしこれを divMod の余りとするのも >> ありそうに思いますがこれを % にした方がよいでしょうか。それとも >> しない方がよいでしょうか。 > > % 演算子に対応させるかどうかについては、とても悩みどころですね。 > 一貫して % は mod の別名としてしまうのもアリだと思います。 > そうしないと定める事も間違いではないと思いますが、 > その場合どのような演算になるか改めて定義が必要ですね。 -- Kenta Murata OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC 本を書きました!! 『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22 E-mail: [hidden email] twitter: http://twitter.com/mrkn/ blog: http://d.hatena.ne.jp/mrkn/ |
|
In reply to this post by Kenta Murata
豊福です。
> In[17]:= Exp[N[100000000, 1]] > Out[17]= 0.*10^43429455 0.* というのが求める有効桁数に応えられないということですかね。 見過ごしてましたが > In[2]:= Mod[N[100000000, 1], 3] > Out[2]= 0.*10^7 こちらも 0.* でしたか。 http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/5776 にごとけんさんによる Mathematica に関する精度の取り扱いに関する 報告がありますね。そんな話があったことすっかり忘れてました。 --- |
|
In reply to this post by wanabe
遠藤です。
2010年2月8日8:51 wanabe <[hidden email]>: >> これはやはりバグだと思います。 > (中略) >> よってこのパッチはあてるべきです。rubyspec のエラーも消えます。 >> >> ワナベさん、お願いしていいでしょうか。 > > 大変申し訳ありません。 > 情けない話なのですが、パッチの意味を覚えておらず判断できかねます。 実は 1 年半前のメールだと気がつかずに返信したのは秘密です。 > さらに議論を見ると、疑問の余地のないバグではなく意味論・仕様に踏み込むようですので > メンテナまたはまつもとさんの決定が必要ではないかと思います。 そのようですね。ただ、現在の bigdecimal は細かいところ議論しだすと キリがないので、とりあえずは rubyspec をパスさせたいと思います。 作者本人である小林さんのパッチ [ruby-dev:40358] が良さそうなので、 こちらをコミットさせて頂きます。むらたさんがコミッタ就任したら好き なようにしてください。 個人的な意見を言うと、bigdecimal は互換性を保ちながら改善していく のが困難だと思います。下手に「改善」して Rails あたりを撃墜して しまうよりは、bigdecimal はバグも含めて凍結してしまい、1.9.3 以降 で alternative を導入、2.0 で gem 化する、という計画がいいのでは ないかと思ってます。個人的な意見でした。 -- Yusuke ENDOH <[hidden email]> |
| Powered by Nabble | Edit this page |
