Java 50: BigDecimal — Money, Precision & RoundingMode
easy⏱ 5 mincoursejava
Why double fails for money
double is binary IEEE-754: 0.1 has no finite binary representation, so 0.1 + 0.2 yields 0.30000000000000004. Accumulating such errors over a ledger drifts by cents. BigDecimal stores an exact BigInteger unscaled value and a scale (number of decimal places), so 0.1 is exactly 0.1. Never use double/float for currency.
System.out.println(0.1 + 0.2); // 0.30000000000000004
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b)); // 0.3 (exact)
Construct from String, never double
new BigDecimal(0.1) captures the double's error: 0.1000000000000000055511.... new BigDecimal("0.1") is exactly 0.1. Always build BigDecimal from a String (or BigDecimal.valueOf(double), which routes through Double.toString). Equality is also a trap: equals compares scale too, so 2.0 ≠ 2.00; use compareTo() == 0 for numeric equality.
RoundingMode & scale
Division can be non-terminating, so divide requires a scale + RoundingMode or it throws ArithmeticException. HALF_UP rounds .5 away from zero (school rounding). HALF_EVEN (banker's rounding) rounds .5 to the nearest even digit — it's the default for money because it removes the systematic upward bias of always rounding .5 up over millions of transactions. setScale(2, HALF_EVEN) snaps to cents.
BigDecimal price = new BigDecimal("10.005");
price.setScale(2, RoundingMode.HALF_UP); // 10.01
price.setScale(2, RoundingMode.HALF_EVEN); // 10.00 (round to even)
Exact money math
(1) Prove the bug: print that 0.1+0.2 as double is NOT 0.3, but exact decimal addition gives exactly 0.3. (2) Sum a price list [19.99, 5.01, 0.10, 0.20] exactly to 25.30. (3) Split a $10.00 bill 3 ways with HALF_EVEN to cents and show the remainder cent is allocated (so the parts sum back to 10.00). Print all three results.
Store money as integer cents when you can
If your domain never needs sub-cent precision, store money as a long of minor units (cents) and only format on output. It's faster than BigDecimal, has no rounding-mode surprises in arithmetic, and dodges the equals-vs-compareTo trap. Reach for BigDecimal when you need configurable scale, currency exchange, or tax fractions.
Quiz: BigDecimal equals vs compareTo
Is new BigDecimal("2.0").equals(new BigDecimal("2.00")) true? No — equals also compares scale (1 vs 2 decimals), so they're unequal despite being numerically the same. Use a.compareTo(b) == 0 for value equality. This subtlety bites when BigDecimals are used as HashMap keys or in assertEquals.