BigDecimal
java.math.BigDecimal represents decimal numbers exactly. Where double stores a binary approximation of 0.1, a BigDecimal stores the digits 1 and the fact that they sit one place after the decimal point. Arithmetic on BigDecimal follows the decimal rules people use on paper, and rounding happens only where you ask for it, using a rounding mode you choose. This makes BigDecimal the standard type for money, prices, taxes, interest, and any calculation whose results must match an accountant's, a regulator's, or a customer's expectations to the last paisa.
Why double Is Not Suitable for Money
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2"))); // 0.3
A tiny binary error in one calculation may look harmless, but invoices sum many items, apply percentages, and are compared against bank records. Errors accumulate, totals differ by a paisa from what a customer computes by hand, and equality checks fail. BigDecimal removes the problem at its source.
How BigDecimal Stores a Value
A BigDecimal is an unscaled value (an arbitrary-size integer) plus a scale (how many digits are after the decimal point):
123.45 = unscaled value 12345, scale 2
2.50 = unscaled value 250, scale 2
BigDecimal x = new BigDecimal("123.4500");
x.scale(); // 4: digits after the decimal point
x.precision(); // 7: total significant digits
x.unscaledValue(); // 1234500
Creating BigDecimals Correctly
BigDecimal a = new BigDecimal("1499.99"); // from a String: exact
BigDecimal b = BigDecimal.valueOf(1499); // from a long: exact
BigDecimal c = BigDecimal.valueOf(0.1); // from a double, via its shortest String form: 0.1
BigDecimal d = new BigDecimal(0.1); // DON'T: captures the double's exact binary value
System.out.println(new BigDecimal(0.1));
// 0.1000000000000000055511151231257827021181583404541015625
The double constructor faithfully copies the binary approximation, which defeats the purpose. Create BigDecimal values from strings (or from integers), or use BigDecimal.valueOf(double) when a double is all you have.
Constants are available: BigDecimal.ZERO, ONE, TWO, TEN.
Immutability and Method-Based Arithmetic
Like BigInteger, BigDecimal is immutable and uses methods instead of operators. Every operation returns a new object:
BigDecimal price = new BigDecimal("79.50");
BigDecimal qty = BigDecimal.valueOf(3);
BigDecimal amount = price.multiply(qty); // 238.50
BigDecimal withFee = amount.add(new BigDecimal("15")); // 253.50
BigDecimal diff = withFee.subtract(amount); // 15.00
Forgetting to keep the result (amount.add(fee); on its own line) is the most common mistake.
Scales combine predictably: add and subtract use the larger scale; multiply adds the scales (79.50 × 3 = 238.50, scale 2 + 0).
Division Requires a Rounding Decision
Some divisions have no finite decimal result:
BigDecimal.ONE.divide(BigDecimal.valueOf(3));
// ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
Instead of silently approximating, BigDecimal makes you state the scale and rounding mode:
BigDecimal third = BigDecimal.ONE.divide(BigDecimal.valueOf(3), 4, RoundingMode.HALF_UP); // 0.3333
Always pass a scale and RoundingMode (or a MathContext) to divide unless you are certain the result terminates.
Rounding with setScale
setScale(n, mode) rounds a value to n decimal places:
new BigDecimal("2.345").setScale(2, RoundingMode.HALF_UP); // 2.35
| Mode | Rule | 2.345 | 2.355 | −2.345 |
|---|---|---|---|---|
HALF_UP | Round half away from zero (school rounding) | 2.35 | 2.36 | −2.35 |
HALF_EVEN | Round half to the even neighbor ("banker's rounding") | 2.34 | 2.36 | −2.34 |
HALF_DOWN | Round half toward zero | 2.34 | 2.35 | −2.34 |
DOWN | Truncate toward zero | 2.34 | 2.35 | −2.34 |
UP | Away from zero | 2.35 | 2.36 | −2.35 |
CEILING | Toward +∞ | 2.35 | 2.36 | −2.34 |
FLOOR | Toward −∞ | 2.34 | 2.35 | −2.35 |
UNNECESSARY | Throw if rounding would be needed |
HALF_UP matches what most people expect on invoices. HALF_EVEN avoids a systematic upward bias when rounding many values, which is why it is common in banking and statistics. The correct mode is often a business or legal rule (tax regulations frequently specify one), so make it an explicit, documented choice.
Comparing: compareTo, Not equals
equals compares both value and scale; compareTo compares numeric value only:
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
a.equals(b); // false: different scales
a.compareTo(b) == 0; // true: numerically equal
Use compareTo for numeric comparisons (> 0, < 0, == 0). The difference also affects collections: 2.0 and 2.00 are different keys in a HashMap or HashSet, but the same key in a TreeMap or TreeSet. Normalize first with setScale (or stripTrailingZeros()) if values from different sources will be compared or stored as keys.
Other comparison helpers: signum(), max(other), min(other), abs(), negate().
Displaying Values
new BigDecimal("2108.07").toPlainString(); // "2108.07"
new BigDecimal("1E+3").toString(); // "1E+3" scientific form can appear
new BigDecimal("1E+3").toPlainString(); // "1000"
String.format("%,.2f", new BigDecimal("2108.07")); // "2,108.07"
NumberFormat.getCurrencyInstance(Locale.forLanguageTag("en-IN")).format(new BigDecimal("2108.07")); // ₹2,108.07
Use toPlainString() when sending values to other systems, since toString() may use exponent notation for some scales.
Performance and Alternatives
BigDecimal is much slower than double and creates many objects. For most business applications that cost is irrelevant compared with database and network time. Where performance matters greatly, a common alternative for money is a long holding the amount in the smallest currency unit (paise), with the conversion to rupees done only for display. Databases offer DECIMAL/NUMERIC columns, which JDBC maps to BigDecimal.
Practical Example: Invoice with GST and a Fair Split
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
public class BigD {
static final BigDecimal GST = new BigDecimal("0.18");
static List<BigDecimal> split(BigDecimal total, int people) {
BigDecimal share = total.divide(BigDecimal.valueOf(people), 2, RoundingMode.DOWN);
BigDecimal remainder = total.subtract(share.multiply(BigDecimal.valueOf(people)));
List<BigDecimal> shares = new ArrayList<>();
for (int i = 0; i < people; i++) {
BigDecimal extra = remainder.compareTo(BigDecimal.ZERO) > 0 ? new BigDecimal("0.01") : BigDecimal.ZERO;
shares.add(share.add(extra));
remainder = remainder.subtract(extra);
}
return shares;
}
public static void main(String[] args) {
record Line(String item, String price, int qty) { }
List<Line> lines = List.of(new Line("Course: Java", "1499.00", 1), new Line("Notebook", "79.50", 3), new Line("Pen", "12.25", 4));
BigDecimal subtotal = BigDecimal.ZERO;
for (Line l : lines) {
BigDecimal amount = new BigDecimal(l.price()).multiply(BigDecimal.valueOf(l.qty()));
subtotal = subtotal.add(amount);
System.out.printf("%-14s %8s x %d = %9s%n", l.item(), l.price(), l.qty(), amount);
}
BigDecimal tax = subtotal.multiply(GST).setScale(2, RoundingMode.HALF_UP);
BigDecimal total = subtotal.add(tax);
System.out.printf("%-26s %9s%n%-26s %9s%n%-26s %9s%n", "Subtotal", subtotal, "GST 18%", tax, "Total", total);
System.out.println("Split 4 ways: " + split(total, 4));
}
}
Output:
Course: Java 1499.00 x 1 = 1499.00
Notebook 79.50 x 3 = 238.50
Pen 12.25 x 4 = 49.00
Subtotal 1786.50
GST 18% 321.57
Total 2108.07
Split 4 ways: [527.02, 527.02, 527.02, 527.01]
Every price is created from a String, so each line amount is exact. GST on 1,786.50 is exactly 321.570; setScale(2, HALF_UP) rounds it to paise at the single point where the business rule says rounding happens. The total therefore matches what a person would calculate by hand.
The split shows a classic money problem. ₹2,108.07 divided by 4 is 527.0175, which cannot be paid exactly. Rounding every share to 527.02 would collect 2,108.08, a paisa too much, and rounding down would collect 2,108.04, three paise short. split rounds each share down, computes the leftover (0.03), and adds one paisa to the first three shares, so the shares always add up to exactly the total. Payment systems use this same technique when dividing bills, EMIs, or refunds.