Java float精度に関するいくつかの問題に気づきました
Float.parseFloat("0.0065") - 0.001 // 0.0055000000134110451
new Float("0.027") - 0.001 // 0.02600000000700354575
Float.valueOf("0.074") - 0.001 // 0.07399999999999999999
Float
だけでなく、Double
にも問題があります。
誰かが舞台裏で何が起こっているのか説明できますか、そしてどうすれば正確な数を得ることができますか?これらの問題に対処するときにこれを処理する正しい方法は何でしょうか?
問題は、float
の精度が有限であるということです。 0.0065
を正確に表すことはできません。 (もちろん、double
についても同じことが言えます。精度は高くなりますが、それでも有限です。)
上記の問題をより明白にする別の問題は、0.001
がdouble
ではなくfloat
であるため、float
がに昇格することです。減算を実行するためのdouble
、そしてもちろんその時点で、システムはdouble
couldが最初に表した欠落した精度を回復する方法がありません。これに対処するには、次のように記述します。
float f = Float.parseFloat("0.0065") - 0.001f;
0.001f
の代わりに0.001
を使用します。
すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと を参照してください。あなたの結果は私には正しいように見えます。
浮動小数点数の動作が気に入らない場合は、代わりに BigDecimal のようにしてみてください。
あなたは正しい結果を得ています。 0.027のようなfloat
は正確には存在せず、またそのようなdouble
も存在しません。 float
またはdouble
を使用すると、常にこれらのエラーが発生します。
float
とdouble
は次のように格納されます2進小数:1/2 + 1/4 +1/16のようなもの...すべての10進値を取得することはできません有限精度の2進小数として正確に格納されます。数学的には不可能です。
唯一の代替方法は、BigDecimal
を使用することです。これを使用して、正確な10進値を取得できます。
浮動小数点リテラルは、文字
F
またはf
で終わる場合、float型になります。それ以外の場合、そのタイプはdoubleであり、オプションで文字D
またはd
で終了できます。
だから私はあなたのリテラルだと思います(0.001
)はdoubleであり、floatからdoubleを減算しています。
代わりにこれを試してください:
System.out.println((0.0065F - 0.001D)); // 0.005500000134110451
System.out.println((0.0065F - 0.001F)); // 0.0055
...そしてあなたは得るでしょう:
0.005500000134110451
0.0055
したがって、リテラルにF
サフィックスを追加すると、より良い結果が得られるはずです。
Float.parseFloat("0.0065") - 0.001F
new Float("0.027") - 0.001F
Float.valueOf("0.074") - 0.001F
Floatを文字列に変換してから、BigDecimalを使用します。
This リンクはそれをよく説明しています
new BigDecimal(String.valueOf(yourDoubleValue));
ただし、エラーが発生するため、BigDecimaldoubleコンストラクターは使用しないでください。
任意精度が必要な場合は、floatまたはdoubleではなくBigDecimalを使用してください。 floatを使用すると、この種のあらゆる種類の丸めの問題が発生します。
余談ですが、BigDecimalのfloat/doubleコンストラクターは同じ問題が発生するため、使用しないように十分に注意してください。代わりにStringコンストラクターを使用してください。
浮動小数点は10進数を正確に表すことができません。 Javaで数値を正確に表現する必要がある場合は、Java.math.BigDecimalクラスを使用する必要があります。
BigDecimal d = new BigDecimal("0.0065");