Traps

89. Floating-Point Equality

Never use == to compare double or float values; 0.1 + 0.2 != 0.3 because of IEEE 754 binary representation; use a tolerance comparison or BigDecimal for exact decimal arithmetic (e.g., money).

Java 21 (core language)

Explanation

IEEE 754 binary floating-point cannot represent many decimal fractions exactly. 0.1 in binary is a repeating fraction โ€” the stored value is slightly off. Arithmetic on these inexact values accumulates error, so 0.1 + 0.2 evaluates to 0.30000000000000004, not 0.3.

For comparisons: use Math.abs(a - b) < epsilon where epsilon is an appropriate tolerance for your domain. There is no single right epsilon โ€” choose based on the magnitude of the values and the acceptable error.

For money and exact decimal arithmetic: use java.math.BigDecimal. It stores decimal numbers exactly (as arbitrary-precision decimal strings) and supports exact addition, subtraction, multiplication, and division (with explicit rounding modes). Never store money as double โ€” you will have rounding errors in cents.

Example

import java.math.BigDecimal;
import java.math.RoundingMode;

public class FloatingPointEquality {
    public static void main(String[] args) {
        // The classic trap
        double a = 0.1 + 0.2;
        double b = 0.3;
        System.out.println("0.1 + 0.2 == 0.3 : " + (a == b));     // false!
        System.out.printf( "0.1 + 0.2 = %.17f%n", a);              // 0.30000000000000004
        System.out.printf( "0.3       = %.17f%n", b);              // 0.29999999999999999

        // Tolerance comparison
        double EPSILON = 1e-9;
        System.out.println("Approx equal:     " + (Math.abs(a - b) < EPSILON)); // true

        // Double.compare: handles NaN and -0.0 correctly
        double x = 1.0 / 3.0;
        double y = 1.0 / 3.0;
        System.out.println("Double.compare:   " + (Double.compare(x, y) == 0)); // true (same computation)

        // NaN traps
        double nan = Double.NaN;
        System.out.println("NaN == NaN:       " + (nan == nan));         // false! (IEEE 754)
        System.out.println("Double.isNaN:     " + Double.isNaN(nan));    // true (correct check)

        // -0.0 == 0.0 but they differ in IEEE 754
        System.out.println("-0.0 == 0.0:      " + (-0.0 == 0.0));        // true!

        // MONEY: use BigDecimal for exact decimal arithmetic
        BigDecimal price = new BigDecimal("19.99");
        BigDecimal tax   = new BigDecimal("0.08");
        BigDecimal total = price.multiply(BigDecimal.ONE.add(tax))
                               .setScale(2, RoundingMode.HALF_UP);
        System.out.println("Price with tax:   $" + total); // $21.59

        BigDecimal tenth = new BigDecimal("0.1");
        BigDecimal twoTenths = new BigDecimal("0.2");
        System.out.println("0.1 + 0.2 (BD):   " + tenth.add(twoTenths));  // 0.3 exactly
        System.out.println("Equal (BD):        " + tenth.add(twoTenths).compareTo(new BigDecimal("0.3")) == 0); // true
    }
}

Sources