If you have used JavaScript or Python you may already know this one, but it is worth seeing in Java because double is the default type for decimal literals.
public class Precision {
public static void main(String[] args) {
System.out.println("0.1 + 0.2 = " +(0.1 + 0.2));
System.out.println("is it == 0.3 ? " +((0.1 + 0.2) == 0.3));
System.out.println("0.1f + 0.2f = " +(0.1f + 0.2f));
}
}
0.1 + 0.2 = 0.30000000000000004 is it == 0.3 ? false 0.1f + 0.2f = 0.3
The reason is that a double stores values in binary, and 0.1 has no exact binary representation, just as one third does not. The stored value is extremely close, and adding the two slightly-wrong approximations lands just above 0.3. Printing shows all seventeen digits, so the error is visible.
Two more precision facts worth knowing:
float has roughly 7 significant digits and double roughly 15 to 17, so float loses the error sooner. That is why 0.1f + 0.2f prints a tidy 0.3: the answer is wrong in the same direction, it just has too few digits left to show it.
A float cannot represent every int. (float) 16777217 prints 1.6777216E7, which is not the number you started with. The fix is to convert the other way, to double, which can hold every int exactly.
For money, never use double. Use BigDecimal, or store an amount of cents as a long.
import java.math.BigDecimal;
BigDecimal total = new BigDecimal("0.10").add(new BigDecimal("0.20"));
System.out.println(total);
// 0.30, exactly
System.out.println(total.compareTo(new BigDecimal("0.30")) == 0);
// true
Note the string constructor. Writing new BigDecimal(0.1) imports the same binary approximation error you were trying to escape.