double を使用するときのモジュラス演算子での Java の奇妙な動作にどのように対処しますか?
たとえば、 の結果は であると予想3.9 - (3.9 % 0.1)
されますが3.9
(実際、Googleは私が頭がおかしくなっているわけではないと言っています)、Java で実行すると3.8000000000000003
.
これは、Java が double を格納および処理する方法の結果であることは理解していますが、これを回避する方法はありますか?
正確な結果が必要な場合は、正確な型を使用してください。
double val = 3.9 - (3.9 % 0.1);
System.out.println(val); // 3.8000000000000003
BigDecimal x = new BigDecimal( "3.9" );
BigDecimal bdVal = x.subtract( x.remainder( new BigDecimal( "0.1" ) ) );
System.out.println(bdVal); // 3.9
なぜ 3.8000...003 なのですか? Java は FPU を使用して結果を計算するためです。3.9 を IEEE 倍精度表記で正確に格納することは不可能であるため、代わりに 3.89999... を格納します。そして 3.8999%0.01 は 0.09999 を返します... したがって、結果は 3.8 より少し大きくなります。
Java言語仕様から:
% 演算子によって計算される浮動小数点剰余演算の結果は、IEEE 754 によって定義された剰余演算によって生成される結果と同じではありません。IEEE 754 剰余演算は、切り捨て除算ではなく丸め除算から剰余を計算します。そのため、その動作は通常の整数剰余演算子の動作とは異なります。代わりに、Java プログラミング言語では、整数剰余演算子と同様の方法で動作するように、浮動小数点演算で % を定義しています。これは、C ライブラリ関数 fmod と比較できます。IEEE 754 剰余演算は、ライブラリ ルーチン Math.IEEEremainder によって計算できます。
つまり、Java では剰余の計算に関与する除算の結果を丸めるのに対し、IEEE754 では除算の結果を切り捨てることを指定しているためです。この特定のケースは、この違いを非常に明確に示しているようです。
Math.IEEEremainder を使用して、期待どおりの答えを得ることができます。
System.out.println(3.9 - (3.9 % 0.1));
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));
java.math.BigDecimal とそのメソッドdivideAndRemainder ()を使用できます。
扱っている小数の量がわかっている場合は、最初に整数に変換してみることができます。これは、浮動小数点の不正確さの典型的なケースです。する代わりに、次の3.9 % 0.1
ようなことをしています3.899 % 0.0999