double r = 11.631;
double theta = 21.4;
デバッガーでは、これらは11.631000000000000
および21.399999618530273
として表示されます。
どうすればこれを回避できますか?
私は Joelの説明 が好きでした。これはExcel2007の同様のバイナリ浮動小数点精度の問題を扱います。
最後に01100110 0110がたくさんあるのがわかりますか?これは、0.1にバイナリでの正確な表現がないためです...これは繰り返しの2進数です。これは、1/3が10進数で表現されていないようなものです。 1/3は0.33333333であり、3を永遠に書き続ける必要があります。忍耐力を失うと、何かが不正確になります。
したがって、10進数で、3 * 1/3を実行しようとして、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位に四捨五入された値、またはより正確な値が表示される可能性があります。一部のデバッガーは、開発者の心を読み取る際に他のデバッガーよりも優れた仕事をしますが、それは2進浮動小数点数の根本的な問題です。
精度の限界で安定性が必要な場合は、固定小数点decimal
型を使用します。オーバーヘッドがあり、浮動小数点に変換する場合は明示的にキャストする必要があります。浮動小数点に変換すると、気になる不安定性が再び発生します。
あるいは、それを乗り越えて、浮動小数点演算の限られた精度で作業することを学ぶことができます。たとえば、丸めを使用して値を収束させたり、イプシロン比較を使用して許容誤差を記述したりできます。 「イプシロン」は、許容誤差を定義するために設定する定数です。たとえば、2つの値が互いに0.0001以内にある場合、それらを等しいと見なすことができます。
演算子のオーバーロードを使用してイプシロンの比較を透過的にすることができると思います。それはとてもクールだろう。
仮数指数表現の場合、表現可能な精度内にとどまるようにEPSILONを計算する必要があります。数Nの場合、イプシロン= N/10E + 14
System.Double.Epsilon
は、Double
型の表現可能な最小の正の値です。私たちの目的には小さすぎます。読む 同等性テストに関するMicrosoftのアドバイス
私は以前にこれに出くわしました( 私のブログで )-驚きは「不合理な」数が異なることである傾向があると思います。
ここでの「不合理」とは、この形式では正確に表現できないという事実を指しているだけです。実数(π-円周率など)は、正確に表現することはできません。
ほとんどの人は、1/3が10進数で機能しないことに精通しています:0.3333333333333 .. ..
奇妙なことに、1.1はフロートでは機能しません。人々は、10進値の考え方から、浮動小数点数で機能することを期待しています。
1.1は11x 10 ^ -1です
実際に彼らがベース2にいるとき
1.1は154811237190861x 2 ^ -47です
それを避けることはできません。1/ 3と同じように、いくつかのフロートが「不合理」であるという事実に慣れる必要があります。
21.399999618530273は21.4の単精度(浮動小数点)表現であるように私には思えます。デバッガーがdoubleからfloatのどこかにキャストダウンしているようです。
これを回避する1つの方法は、 [〜#〜] bcd [〜#〜] などの10進数を表す別の方法を使用するライブラリを使用することです。
固定バイト数の浮動小数点数を使用しているため、これを回避することはできません。実数とその限定された表記法の間に同型写像はあり得ません。
しかし、ほとんどの場合、それを単に無視することができます。 21.4 == 21.4は、同じエラーで同じ数値であるため、引き続きtrueになります。ただし、floatとdoubleのエラーが異なるため、21.4f == 21.4は真ではない可能性があります。
固定精度が必要な場合は、固定小数点数を試す必要があります。または整数ですら。たとえば、デバッグページャーに渡すためにint(1000 * x)をよく使用します。
Javaを使用していて、精度が必要な場合は、浮動小数点の計算にBigDecimalクラスを使用します。低速ですが安全です。
気になる場合は、デバッグ中に一部の値を表示する方法をカスタマイズできます。注意して使用してください:-)
Javadocによると
「数値演算子のオペランドの少なくとも1つがdouble型の場合、
演算は64ビット浮動小数点演算を使用して実行され、その結果は
数値演算子はdouble型の値です。他のオペランドがdoubleでない場合は、doubleです。
[。