【Java学習|初心者向け】Javaの浮動小数点数比較における「0.1 == 0.1」の落とし穴

1. 導入:なぜ浮動小数点数の比較は危険なのか

Javaでプログラミングをしていると、数値の比較は日常的に行います。しかし、整数型(intなど)とは異なり、浮動小数点数型(floatやdouble)を「==」演算子で直接比較するのは非常に危険です。期待した結果が得られず、バグの原因になることが多々あります。「0.1」という単純な数値ですら、コンピュータ内部では正確に表現できない場合があり、これがシステム上の重大なエラーを引き起こす可能性があるからです。

2. 基礎知識:浮動小数点数の仕組み

Javaの浮動小数点数は、IEEE 754という国際規格に基づいています。簡単に言うと、数値を「2進数の分数」として表現します。
私たちが普段使う「0.1」という10進数は、2進数に変換すると「0.000110011…」と無限に続く循環小数になります。コンピュータのメモリには限りがあるため、この無限に続く数値をどこかで切り捨てて保存します。
その結果、float型とdouble型では精度(桁数)が異なるため、同じ「0.1」と書いても内部的にはわずかに異なる値として保持されるのです。これが「0.1f == 0.1d」がfalseになる理由です。

3. 実装/解決策:正しい比較方法

浮動小数点数を比較する際の鉄則は、「==」を使わずに「許容誤差(イプシロン)」を設けて比較することです。2つの数値の差が、無視できるほど十分に小さいかどうかを確認します。

4. サンプルプログラム

以下のコードを実行して、直接比較の危険性と、推奨される比較方法を確認してください。

public class FloatComparison {
    public static void main(String[] args) {
        float f = 0.1f;
        double d = 0.1d;

        // 1. 直接比較の危険性
        System.out.println("--- 直接比較 ---");
        System.out.println("0.1f == 0.1d の結果: " + (f == d)); // falseになる

        // 2. 推奨される比較方法(許容誤差を指定)
        System.out.println("\n--- 許容誤差を用いた比較 ---");
        double epsilon = 0.00001d; // 誤差の範囲を設定
        boolean isEqual = Math.abs(f - d) < epsilon;
        
        // 差が許容範囲内であれば等しいとみなす
        System.out.println("誤差を考慮した比較結果: " + isEqual); 
    }
}

5. 応用・注意点:現場でのベストプラクティス

実務において「お金の計算」や「厳密な精度」が求められる場合は、floatやdoubleを使ってはいけません。その際は java.math.BigDecimal クラス を使用してください。BigDecimalは数値を文字列として保持し、10進数計算を正確に行うことができます。

また、もし単に「近い値かどうか」を判定したい場合は、上記の通り「Math.abs(a - b) < 誤差」という手法が一般的です。現場では、この「誤差」をどれくらいに設定するかが重要になります。用途に応じて適切な閾値を決めるようにしましょう。

コメント

タイトルとURLをコピーしました