🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1810)⚡ Quick Fix Works 80% of the time
Always call divide with an explicit scale and rounding mode.
BigDecimal third = total.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);🧠 Why this Happens
Tap to expand the deep technical explanation
BigDecimal represents exact fixed-point decimals. Its no-arg divide() promises the mathematical quotient EXACTLY; when the true expansion is infinite (most divisions, e.g. /3, /7), no finite digit string satisfies the promise, so divide() throws rather than choosing digits for you. Teams hit this migrating from double math (where 1.0/3 quietly became 0.333...) to BigDecimal for currency correctness.
The HITEC City Parking Spot Analogy:
Asked to write 1÷3 out IN FULL on a sticky note: the digits never end, so the writer gives up rather than lie about the last digit.
🔁 How to Reproduce Confirm this is your error
BigDecimal one = new BigDecimal("1"); one.divide(new BigDecimal("3")); // Non-terminating decimal expansion...
🛠️ Solutions (5 Ways to Fix)
Pass scale plus RoundingMode.HALF_UP
👉 Use this for money and reporting figures where two decimals are the contract.
Explicit scale makes every division terminate deterministically; HALF_UP matches common financial rounding expectations.
BigDecimal avg = revenue.divide(units, 2, RoundingMode.HALF_UP);Use MathContext for scientific precision budgets
👉 Use this if values need significant-digit semantics rather than decimal places.
divide(divisor, MathContext.DECIMAL64) caps precision like a scientific calculator, trading exactness for guaranteed termination.
import java.math.MathContext;
BigDecimal ratio = a.divide(b, MathContext.DECIMAL64);Choose HALF_EVEN (banker rounding) for aggregates
👉 Use this if summing many rounded shares must not drift upward.
HALF_EVEN rounds ties to the nearest even digit, statistically canceling bias across thousands of splits - standard in payment ledgers.
amount.divide(count, 2, RoundingMode.HALF_EVEN)Keep BigDecimal end-to-end, never round-trip through double
👉 Use this if fixing precision after discovering double drift.
Converting via double reintroduces binary-float noise (0.1 becomes 0.1000000000000000055...); parse from String or use BigDecimal.valueOf(double) only at boundaries.
BigDecimal d = BigDecimal.valueOf(0.1); // uses Double.toString canonical form
BigDecimal s = new BigDecimal("0.1"); // preferred: exact literalPin known tricky quotients in tests
👉 Use this if finance calculations changed scales recently.
Asserting thirds, sevenths and repeating decimals locks scale/rounding policy into CI so refactors cannot silently change cents.
assertEquals(new BigDecimal("0.33"),
new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP));📋 Version Notes
Message and behavior stable since the BigDecimal rewrite.
Unchanged. Note pow() with huge exponents throws a sibling ArithmeticException when exceeding preferred precision without MathContext.
🛡️ How to Prevent This Next Time
Ban the one-arg divide() in review; make scale+RoundingMode mandatory wherever BigDecimal division appears.