編集:浮動小数点演算が正確ではないことは知っています。そして、算数は私の問題でさえありません。追加により、私が期待した結果が得られます。8099.99975f
しません。
だから私はこの小さなプログラムを持っています:
public class Test {
public static void main(String[] args) {
System.out.println(8099.99975f); // 8099.9995
System.out.println(8099.9995f + 0.00025f); // 8100.0
System.out.println(8100f == 8099.99975f); // false
System.out.println(8099.9995f + 0.00025f == 8099.99975f); // false
// I know comparing floats with == can be troublesome
// but here they really should be equal in every bit.
}
}
IEEE 754単精度浮動小数点数として書かれたときに8099.99975
が丸められるかどうかを確認するために書きました。8100
驚いたことに、Java8099.9995
は float リテラル ( 8099.99975f
) として記述された場合に変換します。計算と IEEE 規格を再度確認しましたが、間違いは見つかりませんでした。8100
は as と同じくらい離れて8099.99975
いますが、 is8099.9995
の最後のビットは正しい表現にする必要があります。8100
0
そこで、Java 言語仕様をチェックして、何か見落としがないかどうかを確認しました。簡単に検索したところ、次の2つのことがわかりました。
-
Java プログラミング言語では、すべての浮動小数点演算子が浮動小数点の結果を結果の精度に丸めるかのように、浮動小数点演算が動作する必要があります。不正確な結果は、無限に正確な結果に最も近い表現可能な値に丸めなければなりません。最も近い 2 つの表現可能な値が等しく近い場合は、最下位ビットが 0 の値が選択されます。
-
Java プログラミング言語は、浮動小数点値を整数に変換するときに、ゼロ方向への丸めを使用します [...]。
ここで、float リテラルについて何も言われていないことに気付きました。したがって、float リテラルは、float にキャストすると、float から int へのキャストと同様にゼロに丸められるだけの double である可能性があると考えました。8099.99975f
これにより、がゼロに丸められた理由が説明されます。
上記の小さなプログラムを書いて私の理論を確認したところ、2 つの float リテラルを追加すると8100
、正しい float が計算されることがわかりました。(ここで、8099.9995
and0.00025
は単一の float として正確に表すことができるため、別の結果につながる可能性のある丸めはありません) float リテラルと計算された float の動作が異なることはあまり意味がなかったので、これは私を混乱させました。言語仕様をもう少し調べて、これを見つけました:
浮動小数点リテラルは、ASCII 文字の F または f [...] の接尾辞が付いている場合、float 型です。float [...] 型の要素は、IEEE 754 32 ビット単精度 [...] バイナリ浮動小数点形式を使用して表現できる値です。
これは最終的に、リテラルを IEEE 標準に従って丸める必要があることを示しています。この場合は8100
です。では、なぜ8099.9995
ですか?