乗算0.3と3の結果がScalaで0.89999999999999になるのはなぜですか?
5 に答える
浮動小数点の計算はかなり複雑な問題です。
これは、浮動小数点数の2進表現と関係があり、すべての可能な数を(明らかに)保証するわけではなく、正確な表現を行うことで、操作のエラーにつながる可能性があります。そうです、これらのエラーは伝播する可能性があります。
ここに主題に関するリンクがありますが、これは世の中で最も単純なことではありませんが、主題を理解したい場合は、良い視点を得ることができます。
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
肝心な問題は、与えられた浮動小数点数、有理数に対して、その数のバイナリ表現が与えられた言語で利用可能なものよりも高い精度を必要とする可能性があるということです。
たとえば、doubleを格納するために24ビットを使用しているが、浮動小数点値のバイナリ表現が正確に表現されるために25ビットを必要とする場合、丸め誤差が発生します。
編集:
コメントでPéterTörökが指摘したように、ほとんどの既知のプログラミング言語は、一般的なデータ型(float
-> 32ビット、double
-> 64ビット)に同じ表現を使用するため、言語に関係なく、通常、データ型を考慮して精度を計算できます。
これは、Scalaだけでなく、浮動小数点数にIEEE標準を使用するすべての言語/プラットフォームに当てはまります。
ルビーの例:
0.1.9.2p320 :001 > 0.3 * 3
=> 0.8999999999999999
またはPython:
>>> 0.3 * 3
0.8999999999999999
これはScalaとはほとんど関係ありません!
その理由は、コンピューターが浮動小数点数を表現する方法に関係しています。コンピューターは10進数ではなく、2進数を使用するため、0.3などの数値は正確に表現されませんが、基数2で簡潔に表現できる近似を使用します。その結果、これらの数値に対して実行される操作はわずかに少なくなる可能性があります。あなたが答えを期待するものから。詳細については、浮動小数点を参照してください。
Fraction
必要に応じて、独自の演算を使用してこれらの計算を正確に実行するクラスのようなものを実装することで、これを回避できます。
正確な値が必要な場合は、BigDecimalを使用してください。
BigDecimal(3)*0.3
あなたに0.9を与えます
これは、一般的に使用される浮動小数点値表現の副作用です。
私はよくその問題を助長し、数値を最も近い望ましい精度よりも大きい値に「丸める」単純な関数を作成します。それはCで次のようになります:
int64_t __pow10_arr[18] = { 1l, 10l, 100l, 1000l, 10000l, 100000l,
1000000l, 10000000, 100000000l, 1000000000l, 10000000000l, 100000000000l,
1000000000000l, 10000000000000l, 100000000000000l, 1000000000000000l, 10000000000000000l, 100000000000000000l };
double roundToNfractions ( double val, uint8_t n )
{
val *= __pow10_arr[n];
val += 0.5;
val = (uint64_t) val;
val /= __pow10_arr[n];
return val;
}
あまり効率的ではありませんが、安全に書くことができ、
printf("%.2d", val);
常に;3.00
の代わりに次のような結果が得られるため、うまくいきます。2.99