21.4 のような値に初期化された double 変数が 21.399999618530273 と表示されるのはなぜですか?
-
05-07-2019 - |
質問
double r = 11.631;
double theta = 21.4;
デバッガーでは、これらは次のように表示されます。 11.631000000000000
そして 21.399999618530273
.
これを回避するにはどうすればよいでしょうか?
解決
これらの精度の問題は、浮動小数点数の内部表現で、それを回避するためにできることはあまりありません。
ところで、これらの値を実行時に印刷すると、少なくとも最新のC ++コンパイラを使用すると、依然として正しい結果が得られることがよくあります。ほとんどの操作では、これはそれほど大きな問題ではありません。
他のヒント
私は Joelの説明が気に入りました。これは同様のバイナリフローティングを処理しますExcel 2007のポイント精度の問題:
最後に0110 0110 0110がたくさんあるのを見てください。これは、 0.1 にはバイナリでの正確な表現がない ...繰り返しのバイナリ番号だからです。これは、1/3が10進表記を持たないようなものです。 1/3は0.33333333であり、3を永久に書き続ける必要があります。忍耐力を失うと、何か不正確になります。
だから、3 * 1/3をやろうとして10進数で3を永久に書く時間がなかった場合、結果は1ではなく0.99999999になり、人々はどうなるか想像できます。間違っていることに怒ってください。
次のような値がある場合:
double theta = 21.4;
そしてあなたがしたいこと:
if (theta == 21.4)
{
}
少し賢くなければなりません。シータの値が本当に 21.4に近いかどうかを確認する必要がありますが、必ずしもその値ではありません。
if (fabs(theta - 21.4) <= 1e-6)
{
}
これは部分的にプラットフォーム固有であり、お客様がどのプラットフォームを使用しているかはわかりません。
それは部分的には自分が本当は何なのかを知ることでもある 欲しい 見る。デバッガーは、変数に格納されている正確な値を、ある程度は表示します。私の中で .NET の 2 進浮動小数点数に関する記事, 、 あります C#クラス これにより、絶対的に見ることができます ちょうど double に格納された数値。オンライン バージョンは現在動作していません。別のサイトにアップしてみます。
デバッガーは「実際の」値を認識しているため、何を表示するかを判断する必要があります。小数点以下の桁を四捨五入した値を表示することも、より正確な値を表示することもできます。一部のデバッガは、開発者の心を読み取るという点で他のデバッガよりも優れた仕事をしますが、これは 2 進浮動小数点数に関する根本的な問題です。
精度の限界で安定性が必要な場合は、固定小数点 decimal
タイプを使用します。オーバーヘッドがあり、浮動小数点に変換する場合は明示的にキャストする必要があります。浮動小数点に変換すると、気になると思われる不安定性が再導入されます。
代わりに、それを乗り越えて、浮動小数点演算の限られた精度で 動作することを学ぶことができます。たとえば、丸めを使用して値を収束させたり、イプシロン比較を使用して許容値を記述したりできます。 &quot;イプシロン&quot;公差を定義する設定した定数です。たとえば、2つの値が互いに0.0001以内にある場合、それらを等しいと見なすよう選択できます。
演算子のオーバーロードを使用して、イプシロン比較を透過的にすることができると思います。それはとてもクールです。
仮数表現については、EPSILONは、表現可能な精度内に収まるように計算する必要があります。数値Nの場合、イプシロン= N / 10E + 14
System.Double.Epsilon
は、 Double
型の表現可能な最小の正の値です。私たちの目的には小さすぎます 。 平等テストに関するマイクロソフトのアドバイス
以前にこれに遭遇しました( on私のブログ)-「不合理な」数字が異なることに驚きがちだと思います。
ここで「不合理」とは、この形式では正確に表現できないという事実を指しているだけです。実際の無理数(&#960;-piなど)は正確に表現できません。
ほとんどの人は、1/3が10進数で機能しないことをよく知っています:0.3333333333333 ...
奇妙なことに、1.1はfloatでは機能しません。人々は、10進数値が浮動小数点数で機能することを期待しています。これは、小数点の値がどのように考えられているかが原因です。
1.1は11 x 10 ^ -1
実際にベース2にいるとき
1.1は154811237190861 x 2 ^ -47
それを避けることはできません。1/ 3がそうであるように、いくつかのフロートが「非合理的」であるという事実に慣れる必要があります。
これを回避する方法の1つは、 BCD
21.399999618530273は、21.4の単精度(浮動)表現であると思われます。デバッガーがdoubleからどこかにフロートするようにキャストしているようです。
Javaを使用していて精度が必要な場合は、浮動小数点計算にBigDecimalクラスを使用します。遅いですが安全です。
固定バイト数の浮動小数点数を使用しているため、これを避けることはできません。実数とその限られた表記の間には、単に同型はありません。
ただし、ほとんどの場合、単に無視できます。 21.4 == 21.4は、同じエラーで同じ数字であるため、依然として真になります。ただし、floatとdoubleのエラーは異なるため、21.4f == 21.4は正しくない場合があります。
固定精度が必要な場合は、おそらく固定小数点数を試す必要があります。または整数。たとえば、デバッグページャーに渡すためにint(1000 * x)をよく使用します。
気になる場合は、デバッグ中に一部の値を表示する方法をカスタマイズできます。注意して使用してください:-)
フロートを比較する際にも注意してください。この回答詳細については。
javadocによると
&quot;数値演算子のオペランドの少なくとも1つがdouble型の場合、
演算は64ビット浮動小数点演算を使用して実行され、
数値演算子は、double型の値です。他のオペランドがdoubleでない場合、
最初に幅を広げ(&#167; 5.1.5)、数値プロモーション(&#167; 5.6)でdoubleを入力します。&quot;