15

通貨、料金などを処理するコードを書いています。計算とストレージに BigDecimal クラスを使用する予定ですが、奇妙なことに遭遇しました。

この文:

1876.8 == BigDecimal('1876.8')

false を返します。

これらの値をフォーマット文字列で実行すると、次の"%.13f"ようになります。

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002

2小数点以下の最後の桁にある BigDecimalの余分な部分に注意してください。

BigDecimal は、コンピューターのネイティブ浮動小数点に実数を直接格納することの不正確さに対抗するものだと考えていました。これはどこ2から来たのですか?

4

6 に答える 6

8

そうです、BigDecimal はそれを正しく格納する必要があります。私の最善の推測は次のとおりです。

  • BigDecimal は値を正しく格納しています
  • 文字列フォーマット関数に渡されると、BigDecimal は精度の低い浮動小数点値としてキャストされ、...02 が作成されます。
  • float と直接比較すると、float には 20 をはるかに超える余分な小数点以下の桁があります (従来の float は動作を比較できません)。

いずれにせよ、float と BigDecimal を比較して正確な結果を得ることはまずありません。

于 2009-04-23T18:57:41.007 に答える
7

FPU 10 進文字列の小数部が等しいかどうかを比較しない

問題は、float 値または double 値と小数を含む 10 進定数との等価比較がほとんど成功しないことです。

2 進数の FP 表現で正確な値を持つ 10 進文字列の分数はほとんどないため、通常、等値比較は失敗に終わります。*

あなたの正確な質問に答えるために、これ2は 10 進文字列分数の形式へのわずかに異なる変換から来ていFloatます。分数を正確に表すことができないため、2 つの計算が中間計算で異なる量の精度を考慮し、最終的に結果を 52 ビットの IEEE 754 倍精度仮数に異なる方法で丸める可能性があります。とにかく正確な表現がないため、ほとんど問題ではありませんが、おそらく一方が他方よりも間違っている可能性があります。

特に、1876.8FP オブジェクトで正確に表現することはできません。実際には、0.01 から 0.99 の間で、0.25、0.50、および 0.75 だけが正確なバイナリ表現を持っています。1876.8 を含む他のすべては、永久に繰り返され、52 ビットに丸められます。これは、BigDecimal が存在する理由の約半分です。(理由の残りの半分は、FP データの精度が固定されているためです。場合によっては、それ以上の精度が必要になります。)

したがって、実際のマシン値を 10 進文字列定数と比較したときに得られる結果は、2 進小数部のすべてのビットに依存します... 1/2 52まで... さらに丸めが必要です。

数を生成するプロセス、入力変換コード、またはその他の関連する部分に、ほんの少しでも (へへ、少し、申し訳ありませんが) 不完全なものがある場合、それらは完全に同じには見えません。

IEEE 形式の FPU はその数値を正確に表すことさえできないため、比較は常に失敗するはずであるという議論がなされることさえあります。同じように見えても、実際には同じではありません。左側では、10 進数の文字列が 2 進数の文字列に変換されていますが、ほとんどの数値は正確に変換されていません。右側は、まだ 10 進文字列です。

したがって、フロートと BigDecimal を混在させないでください。1 つの BigDecimal と別の BigDecimal を比較してください。(両方のオペランド浮動小数点数の場合でも、等しいかどうかのテストには細心の注意またはファジー テストが必要です。また、すべてのフォーマットされた数字を信頼しないでください。出力フォーマットでは、分数の右側から余りが繰り出されるため、通常は開始しません。ゼロを見ると、ガベージ値が表示されます。)


*問題: 機械数は x/2 nですが、10 進定数は x/(2 n * 5 m ) です。符号、指数、および仮数としての値は、0 10000001001 1101010100110011001100110011001100110011001100110011...皮肉なことに無限に繰り返されます。FP 演算は完全に正確であり、値に端数がない場合、等値比較は完全に機能します。

于 2012-12-25T19:29:25.293 に答える
3

Davidが言ったように、BigDecimalはそれを正しく保存しています

 p (BigDecimal('1876.8') * 100000000000000).to_i

187680000000000000 を返します

そう、はい、文字列のフォーマットがそれを台無しにしています

于 2009-04-23T19:01:35.303 に答える
2

小数のセントが必要ない場合は、通貨を整数として保存および操作し、表示するときに 100 で割ることを検討してください。浮動小数点での保存と操作の避けられない精度の問題に対処するよりも簡単だと思います。

于 2009-04-23T20:32:14.353 に答える
1

Mac OS Xで、私は実行していますruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

irb(main):004:0> 1876.8 == BigDecimal('1876.8') => true

ただし、Rubyであるため、オブジェクトに送信されるメッセージの観点から考える必要があると思います。これはあなたに何を返しますか:

BigDecimal('1876.8') == 1876.8

この2つは同等ではありません。また、BigDecimalの機能を使用して正確な小数の同等性を判別しようとしている場合は、同等性について尋ねるメッセージの受信者である必要があります。

同じ理由で、フォーマットメッセージをフォーマット文字列に送信してBigDecimalをフォーマットすることも正しいアプローチではないと思います。

于 2009-04-23T19:21:02.527 に答える