Floating-point precision in Java
0.1 + 0.2 != 0.3, NaN != NaN, why money needs BigDecimal.
Binary can't write 0.1
float and double store numbers in binary. Just as 1/3 is 0.3333... forever in decimal, 0.1 is an endless pattern in binary. It gets rounded to the nearest double, leaving a tiny error.
Your turn
What does this print?
System.out.println(0.1 + 0.2);
System.out.println(0.1 + 0.2 == 0.3);0.3 true0.30000000000000004 false0.30000000000000004 true
Show the answer
The two tiny rounding errors add up, and the sum is a different double from 0.3. Java prints the shortest decimal that identifies that exact double: 0.30000000000000004.
Comparing doubles
if (a + b == 0.3) { ... }Exact == fails over a difference in the 17th digit.
if (Math.abs(a + b - 0.3) < 1e-9) { ... }Compare within a small tolerance.
NaN isn't equal to itself
NaN ("not a number", e.g. 0.0 / 0.0) compares unequal to everything, even itself. So x == x is false when x is NaN. Test with Double.isNaN(x).
double nan = 0.0 / 0.0;
nan == nan // false
Double.isNaN(nan) // trueMoney: BigDecimal
BigDecimal stores decimal digits exactly. Create it from a String (or with BigDecimal.valueOf), or count whole cents in a long.
var a = new BigDecimal("0.10");
var b = new BigDecimal("0.20");
a.add(b) // 0.30, exactlynew BigDecimal(0.1)
Passing a double copies its binary error into the BigDecimal, digit for digit. Always pass a String.
new BigDecimal(0.1)
// 0.1000000000000000055511151231257827
// 021181583404541015625
new BigDecimal("0.1") // 0.1Your turn
With doubles, 0.7 + 0.1 gives 0.7999999999999999. What about BigDecimal?
var x = new BigDecimal("0.7");
var y = new BigDecimal("0.1");
System.out.println(x.add(y));0.80.79999999999999990.80
Show the answer
BigDecimal works in decimal, so the sum is exactly 0.8. Both inputs have one digit after the point, so the result does too.
In real projects
Banks, shops and payment systems never store money in double. A fraction-of-a-cent drift, repeated over millions of transactions, breaks accounting. BigDecimal or whole cents in a long are the industry standard.
Key takeaways
- 0.1 + 0.2 is 0.30000000000000004
- Compare doubles with a tolerance, not ==
- NaN != NaN; use Double.isNaN(x)
- Money: new BigDecimal("0.10") or long cents
In 1991 a Patriot missile battery missed an incoming Scud: its clock counted tenths of a second, and 0.1 can't be stored exactly in binary. After 100 hours, the error had grown to about a third of a second.
Practice questions
What does this print?
System.out.println(0.1 + 0.2);- 0.3
- 0.30000000000000001
- 0.299999999999999
- 0.30000000000000004
Check your answer
0.30000000000000004. Java prints the shortest decimal that uniquely identifies the double, and this sum is slightly more than 0.3.
What does this print?
double nan = 0.0 / 0.0;
System.out.println(nan == nan);
System.out.println(Double.isNaN(nan));- true true
- true false
- false true
- false false
Check your answer
false true. By the IEEE 754 rules NaN compares unequal to everything, even itself. Double.isNaN is the reliable test.