だからこれは変だ。私はRuby1.9.3を使用していますが、floatの加算が期待どおりに機能していません。
0.3 + 0.6 + 0.1 = 0.9999999999999999
0.6 + 0.1 + 0.3 = 1
別のマシンでこれを試しましたが、同じ結果が得られました。なぜこれが起こるのか考えてみませんか?
だからこれは変だ。私はRuby1.9.3を使用していますが、floatの加算が期待どおりに機能していません。
0.3 + 0.6 + 0.1 = 0.9999999999999999
0.6 + 0.1 + 0.3 = 1
別のマシンでこれを試しましたが、同じ結果が得られました。なぜこれが起こるのか考えてみませんか?
浮動小数点演算は不正確です。結果を最も近い表現可能な浮動小数点値に丸めます。
つまり、各フロート操作は次のとおりです。
float(a op b) = mathematical(a op b) + rounding-error( a op b )
上記の式で示唆されているように、丸め誤差はオペランドaとbに依存します。
したがって、異なる順序で操作を実行すると、
float(float( a op b) op c) != float(a op (b op c))
言い換えると、浮動小数点演算は結合法則ではありません。
しかし、それらは可換です...
他に述べたように、小数表現0.1(つまり1/10)を2進数表現(つまり1/16 + 1/64 + ...)に変換すると、無限の桁数になります。したがって、float(0.1)は正確に1/10に等しくなく、丸め誤差もあり、2桁の長い系列になります。これは、次の演算にnull以外の丸め誤差があることを説明しています(数学的な結果は表現できません)。浮動小数点で)
これまで何度も言われてきましたが、繰り返しになります。浮動小数点数は、本質的に10進数の近似値です。浮動小数点数の2進数の格納方法が原因で、正確に表現できない10進数がいくつかあります。小さいながらも知覚できる丸め誤差が発生します。
この種の混乱を避けるために、あなたは常にあなたの数をプレゼンテーションのために適切な数の場所にフォーマットするべきです:
'%.3f' % (0.3 + 0.6 + 0.1)
# => "1.000"
'%.3f' % (0.6 + 0.1 + 0.3)
# => "1.000"
これが、通貨値に浮動小数点数を使用するのは危険であり、これらには固定小数点数または通常の整数を使用することをお勧めする理由です。
まず、原文の「0.3」、「。6」、「。1」の数字が浮動小数点数に変換されます。これをa、b、cと呼びます。これらの値は.3、.6、および.1に近いですが、それらと等しくはありませんが、それが異なる結果が表示される直接の理由ではありません。
各浮動小数点算術演算では、小さな丸め誤差、いくつかの小さな数eiが存在する場合があります。したがって、2つの式が計算する正確な数学的結果は次のとおりです。
(a + b + e 0)+ c + e 1および(b + c + e 2)+ a + e3。
つまり、最初の式では、aがbに追加され、わずかな丸め誤差e0があります。次に、cが追加され、わずかな丸め誤差e1があります。2番目の式では、bがcに追加され、わずかな丸め誤差e2があります。最後に、aが追加され、わずかな丸め誤差e3があります。
結果が異なる理由は、e 0 + e1 ≠ e2 + e3であるためです。つまり、aとbを追加した場合に必要な丸めと、bとcを追加した場合に必要な丸め、および/または2つのケースの2回目の追加で必要な丸めが異なっていました。
これらのエラーを管理するルールがあります。ルールを知っている場合は、最終結果のエラーのサイズを制限するルールについて推測することができます。
これは、浮動小数点数が10進数ではなく2進数でエンコードされるため、一般的な制限です。理解するのは難しい場合がありますが、一度理解すれば、このような問題を簡単に回避できます。私はそれを説明するために深く掘り下げたこのガイドをお勧めします。
特にこの問題の場合は、結果を100万分の1の位に四捨五入してみてください。
result = (0.3+0.6+0.1)
=> 0.9999999999999999
(result*1000000.0).round/1000000.0
=> 1.0
順序が重要である理由については、丸めに関係しています。これらの数値が浮動小数点数に変換されると、それらは2進数に変換され、1/3が10進数であるように、すべてが繰り返し分数になります。結果は加算のたびに丸められるため、最終的な答えは加算の順序によって異なります。そのうちの1つでは切り上げが行われ、もう1つでは切り下げが行われるようです。これは不一致を説明します。
これらの2つの答えの実際の違いは、約0.0000000000000001であることに注意してください。
number_with_precision
ビューでは、ヘルパーも使用できます。
result = 0.3 + 0.6 + 0.1
result = number_with_precision result, :precision => 3