BigDecimal for money in Java
Exact decimal math, new BigDecimal("0.1") vs (0.1), compareTo vs equals, scale and RoundingMode.
Counting money in coins
double stores numbers as binary fractions. Just as 1/3 can't be written exactly in decimal (0.333…), 0.1 can't be written exactly in binary, so tiny errors creep in. **BigDecimal stores an exact decimal number** — like counting real coins instead of estimating.
The famous sum
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
0.30000000000000004 and false — binary rounding errors. With new BigDecimal("0.1").add(new BigDecimal("0.2")) you get exactly **0.3**.
Building a BigDecimal
new BigDecimal(0.1)
// 0.1000000000000000055511151231257827…The double 0.1 is already slightly off, and this constructor copies that error exactly.
new BigDecimal("0.1") // exactly 0.1
BigDecimal.valueOf(0.1) // also 0.1Strings are exact. valueOf goes through the double's short text form, so it's fine too.
Scale, equals and compareTo
The scale is the number of digits after the point: 2.0 has scale 1, 2.00 scale 2. **equals compares value AND scale, so they're *not* equal! compareTo compares only the numeric value** — use it to compare amounts.
var a = new BigDecimal("2.0");
var b = new BigDecimal("2.00");
a.equals(b); // false (scale 1 vs 2)
a.compareTo(b) == 0; // trueEqual or not?
What does this print?
var x = new BigDecimal("1.50");
var y = new BigDecimal("1.5");
System.out.println(x.equals(y));
System.out.println(x.compareTo(y));true 0false 0false 1
Show the answer
false then 0 — different scales make equals false, while compareTo returns 0 for the same numeric value.
Rounding on purpose
setScale(2, mode) rounds to 2 decimals using a **RoundingMode you choose. HALF_UP rounds ties up (school rounding). HALF_EVEN (banker's rounding) rounds ties to the even** neighbour, which avoids bias over many sums.
var x = new BigDecimal("0.125");
x.setScale(2, RoundingMode.HALF_UP); // 0.13
x.setScale(2, RoundingMode.HALF_EVEN); // 0.12Dividing into infinity
1 ÷ 3 = 0.333… has no exact decimal form, so divide without a scale throws **ArithmeticException ("Non-terminating decimal expansion"). Give it a scale and a RoundingMode**.
var total = new BigDecimal("1.00");
var three = new BigDecimal("3");
total.divide(three); // ArithmeticException
total.divide(three, 2, RoundingMode.HALF_UP);
// 0.33Money in production
Banks, payment providers and e-commerce backends use BigDecimal (or integer cents) for every amount. Splitting a bill, applying tax, converting currency — each needs an explicit scale and RoundingMode, and auditors will ask which one you chose.
Key takeaways
- new BigDecimal("0.1") is exact; new BigDecimal(0.1) is not
- equals checks value AND scale: 2.0 ≠ 2.00
- compareTo checks only the numeric value
- divide may need a scale and RoundingMode
new BigDecimal(0.1) prints 0.1000000000000000055511151231257827021181583404541015625 — that's the exact value the double 0.1 really holds.
Practice questions
What does this print?
System.out.println(0.1 + 0.2);
System.out.println(new BigDecimal("0.1")
.add(new BigDecimal("0.2")));- 0.3 0.3
- 0.30000000000000004 0.3
- 0.30000000000000004 0.30000000000000004
- 0.3 0.30000000000000004
Check your answer
0.30000000000000004 0.3. double addition shows binary rounding error, while BigDecimal built from strings adds exact decimals.
What does this print?
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
System.out.println(a.equals(b));
System.out.println(a.compareTo(b) == 0);- true true
- false false
- false true
- true false
Check your answer
false true. BigDecimal.equals also compares the scale (1 vs 2 decimal places), so it's false. compareTo compares only the numeric value.