社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

Java で何故 4.0 - 3.10 は 0.90 ではない ?

twitter で流れてきたツイートで以下のようなものがありました。

Why 4.0 - 3.10 not equal to 0.90 ?

public static void main(String args[]) {
  double x = 4.0 - 3.10;
  System.out.println(x == 0.90);
}

結果: false

ツイートに対する返信で、「double や float を比較するのに == を使ってはならない。」というものがありましたが・・・。
実際にこの値を取得してみると [0.8999999999999999] という値が得られます。

なぜ誤差が生じるのか?
  • float型とdouble型は、2進浮動小数点算術を行う。
  • 2進浮動小数点算術は、科学計算と工学計算のために設計されており、広範囲の大きさの数値に対して近似を取得する。

つまりは、2進浮動小数点 で演算しているので、広い範囲の桁数を扱うことになった場合は、近似値を取得しようとするので、その時点で正確な値ではなくなってしまうということだと思っています。

誤差が生じないケース?

0.5(10進数) -> 0.1(2進数)
0.5 のように、2進数に有効桁で変換出来る数値であれば問題ない。

double y = 0.5 - 0.25;
System.out.println(y);  // 0.25
誤差が生じるケース?

0.1(10進数) -> 0.00011001100110011....(2進数)
0.1 のように、2進数では表現ができない(循環小数無限小数)数値だと、どこかで桁の丸めが発生するので、その時点で誤差(丸め誤差)が発生することになる。

double z = 0.30 - 0.20;
System.out.println(z);	// 0.09999999999999998

※ただし、答えが 0.1 になる全ての演算で誤差になる訳ではなさそうです

double p = 0.20 - 0.10;
System.out.println(p);	// 0.1

どこの桁で丸めが発生するかに依存するように思います。

   0.00110011001100 (0.2)
-) 0.00011001100110 (0.1)
   0.00011001100110 (0.1)
結論
  • Java で float型とdouble型の計算結果は正確な値を保証しない

BigDecimalなどのBCDを使うか、許容可能であれば四捨五入で対応する