Why does 0.1 + 0.2 not equal 0.3 in Java, and how should you handle floating-point precision and rounding errors?
answer
- Doubles are binary; 0.1/0.2/0.3 aren't exact → rounding error
- 0.1 + 0.2 == 0.30000000000000004
- Compare with epsilon (absolute vs relative), never ==
- BigDecimal from String for money; double ctor carries the error
- float ~7 digits, double ~15-16; errors accumulate
basics
~20 sDoubles store numbers in binary, and 0.1, 0.2, 0.3 can't be represented exactly, so tiny rounding errors creep in: 0.1 + 0.2 is 0.30000000000000004. Never compare with == — compare within a tolerance, or use BigDecimal for money.
solid answer
~50 sJava's double/float are binary IEEE 754 with a finite number of bits, so any decimal fraction whose value isn't an exact sum of powers of two — including 0.1, 0.2, 0.3 — is stored as the nearest representable binary approximation. The small errors accumulate, so 0.1 + 0.2 produces 0.30000000000000004, and == against 0.3 is false. The fixes depend on context: for comparisons, test |a - b| < epsilon with a sensibly chosen tolerance (absolute or relative), never ==. For exact decimal arithmetic, especially money, use BigDecimal constructed from Strings (new BigDecimal("0.1"), not the double constructor, which carries the error in) and set an explicit RoundingMode/scale. Be aware that float has far less precision (~7 significant digits) than double (~15-16), and that summation order affects accumulated error. For currency, many teams instead store integer minor units (cents).
code
java · 15 linesSystem.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3); // false
// Tolerance-based comparison:
double eps = 1e-9;
System.out.println(Math.abs((0.1 + 0.2) - 0.3) < eps); // true
// BigDecimal — construct from String, set rounding:
import java.math.BigDecimal;
import java.math.RoundingMode;
BigDecimal sum = new BigDecimal("0.1").add(new BigDecimal("0.2"));
System.out.println(sum); // 0.3 (exact)
System.out.println(new BigDecimal(0.1)); // 0.1000000000000000055511151231257827021181583404541015625 (error!)
BigDecimal div = BigDecimal.ONE.divide(new BigDecimal("3"), 4, RoundingMode.HALF_UP);
System.out.println(div); // 0.3333go deeper
Knows 0.1 + 0.2 isn't exactly 0.3 and that you shouldn't compare doubles with ==.
Explains binary representation of decimals, uses an epsilon comparison, and reaches for BigDecimal for money.
Distinguishes absolute vs relative tolerance, knows the BigDecimal String-constructor pitfall and division/RoundingMode rules, and the integer-cents strategy.
Sets numeric standards across a codebase (money type, rounding policy, tolerance utilities), reasons about error accumulation/cancellation, and chooses representations per domain accuracy needs.
## The root cause: binary fractions Java `double`/`float` follow **IEEE 754**, which stores numbers in **base 2** (binary), not base 10. A value is `mantissa × 2^exponent`. Only fractions that are finite sums of powers of two can be stored exactly — e.g. `0.5 = 2^-1`, `0.25 = 2^-2`, `0.75 = 2^-1 + 2^-2`. Decimal `0.1` is **not** such a sum. In binary it is the infinitely repeating `0.0001100110011…`, just like `1/3` is `0.333…` in decimal. Since a `double` has only 52 bits of mantissa, the value is rounded to the **nearest representable** binary number, introducing a tiny error. The same is true of `0.2` and `0.3`. ## Why 0.1 + 0.2 != 0.3 The stored `0.1` and `0.2` are each slightly off; their sum rounds to a `double` that is **not** the same `double` chosen to approximate `0.3`. Concretely: ```java System.out.println(0.1 + 0.2); // 0.30000000000000004 System.out.println(0.1 + 0.2 == 0.3); // false ``` This is not a Java bug; every IEEE 754 language (C, Python, JS, …) shows it. ## Precision limits - `float` (32-bit): ~7 significant decimal digits. - `double` (64-bit): ~15–16 significant decimal digits. Errors **accumulate**: summing many values, subtracting nearly-equal values (*catastrophic cancellation*), or multiplying repeatedly all magnify the rounding error. Even the *order* of additions changes the result. ## How to handle it ### 1. Never use == for fractional comparison Use a tolerance (*epsilon*): ```java static boolean nearlyEqual(double a, double b, double eps) { return Math.abs(a - b) < eps; } ``` - **Absolute** tolerance (`< 1e-9`) is fine for numbers near 1. - **Relative** tolerance (`Math.abs(a-b) <= eps * Math.max(Math.abs(a), Math.abs(b))`) is better across magnitudes, because a fixed absolute epsilon is too large for tiny numbers and too small for huge ones. ### 2. Use BigDecimal for exact decimal math (money!) `BigDecimal` stores an arbitrary-precision **base-10** number, so decimals are exact. ```java new BigDecimal("0.1").add(new BigDecimal("0.2")); // exactly 0.3 ``` **Critical:** construct from a **String**, not a double. `new BigDecimal(0.1)` captures the *binary error* (`0.1000000000000000055…`). Always set scale + `RoundingMode` for division (`divide` throws `ArithmeticException` on a non-terminating result otherwise). ### 3. Integer minor units For money, many systems store **cents (longs)** and only format to dollars at the edge — no fractions, no rounding surprises. ## Other gotchas - **Don't loop with a float/double counter** (`for (double d = 0; d != 1.0; d += 0.1)`) — it may never hit the bound. Use an integer loop variable. - `strictfp` (and, since Java 17, all FP is effectively strict) controls platform-independent rounding. - Printing: `0.1` *displays* as `0.1` because `Double.toString` prints the shortest decimal that round-trips — the stored value is still approximate. ## Summary Floating point trades exactness for range and speed. Treat it as approximate: compare with tolerances, use `BigDecimal`/integer cents where exactness is required, and pick `double` over `float` unless memory/throughput forces otherwise.
- Why is new BigDecimal("0.1") preferred over new BigDecimal(0.1)?The double 0.1 already holds the binary approximation, so new BigDecimal(0.1) captures the full error (0.1000000000000000055...). The String constructor parses the exact decimal you wrote.
- What is the difference between an absolute and a relative tolerance, and when do you need relative?Absolute compares |a-b| to a fixed epsilon; relative scales epsilon by the magnitude of the operands. You need relative when values span very different magnitudes, because a fixed absolute epsilon is too coarse for large numbers and too strict for tiny ones.
saying these in an interview costs you the question
- Using == to compare computed doubles
- Constructing BigDecimal from a double literal (new BigDecimal(0.1))
- Using float/double for currency amounts
- Looping with a floating-point counter and exact bound
- Calling it a Java bug rather than IEEE 754 binary representation