Definition
- Arithmetic mean – sum of all values divided by their count.
- BigDecimal division – calling
divide(divisor)without scale throwsArithmeticExceptionon non-terminating results like 10/3. - Safe divide –
sum.divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN)gives a fixed two-decimal result. - Banker's rounding – HALF_EVEN rounds to the nearest even digit on a tie, reducing systematic bias across large datasets.
- Addition –
a.add(b).add(c)is exact; only the division introduces rounding.
National Bank of India averages Rajesh's three most recent monthly closing balances to decide his interest tier. The bank adds ten, twenty, and thirty thousand, divides by three, and gets twenty thousand. The only care is that the division must round in a defined way.
Understanding the Concepts
What is the problem?
Read three numbers, compute their average, and print it. For input 10, 20, 30 the output is 20.
How do you implement it with BigDecimal?
package com.bank.service;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class AverageBalanceService {
public static BigDecimal average(BigDecimal a, BigDecimal b, BigDecimal c) {
BigDecimal sum = a.add(b).add(c);
return sum.divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN);
}
public static void main(String[] args) {
BigDecimal m1 = new BigDecimal("10000.00");
BigDecimal m2 = new BigDecimal("20000.00");
BigDecimal m3 = new BigDecimal("30000.00");
System.out.println("Average balance: " + average(m1, m2, m3));
}
}
Average balance: 20000.00
Why does dividing by 3 need special care?
Dividing 10000 by 3 gives 3333.333... which never terminates. BigDecimal's divide(divisor) throws ArithmeticException when it cannot represent the result exactly. The overload with scale and rounding mode forces a defined result.
// Wrong: throws ArithmeticException
new BigDecimal("10000").divide(new BigDecimal("3"));
// ArithmeticException: Non-terminating decimal expansion
// Correct: specify scale and rounding
new BigDecimal("10000").divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN);
// 3333.33
Why HALF_EVEN over HALF_UP?
HALF_UP always rounds 0.5 up, which introduces a systematic upward bias across thousands of calculations. HALF_EVEN rounds 0.5 to the nearest even digit, sometimes up and sometimes down, so the bias cancels out over large datasets. This is why it is called banker's rounding.
| Value | HALF_UP | HALF_EVEN |
|---|---|---|
| 2.5 | 3 | 2 (even) |
| 3.5 | 4 | 4 (even) |
| 4.5 | 5 | 4 (even) |
| 5.5 | 6 | 6 (even) |
What are edge cases?
- All three values equal: average equals each value.
- Two zeros and one value: average is value / 3.
- Values that produce a non-terminating quotient: the rounding mode handles it.
What are common mistakes?
// Wrong: using double for money
double avg = (10000.0 + 20000.0 + 30000.0) / 3;
// May drift for other inputs
// Wrong: forgetting rounding mode
sum.divide(new BigDecimal("3")); // ArithmeticException!
// Correct
sum.divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN);
How is a plain average different from a weighted average?
A plain average treats all values equally. A weighted average multiplies each value by a weight (e.g., number of days held). For the three-value equal-weight case, the plain sum-over-three is exactly right.
What should a senior engineer know?
The rounding mode should be a shared constant across the codebase, not chosen per-method. For a time-weighted average balance, each balance is multiplied by the number of days it was held, and the divisor is the total number of days. That is a different calculation but uses the same BigDecimal discipline.
What are common interview follow-up questions?
- What is the difference between BigDecimal equals and compareTo?
- How would you compute the average of N values, not just three?
- When would you use HALF_UP instead of HALF_EVEN?
Visual Overview
┌─────────┐
│ Start │
└────┬────┘
▼
Read A, B, C
│
▼
sum = A.add(B).add(C)
│
▼
avg = sum.divide(3, 2, HALF_EVEN)
│
▼
Print avg → End
Key Takeaway
The average of three numbers is their sum divided by three. The addition is trivial with BigDecimal's add method, but the division is where correctness is decided.
With BigDecimal, calling divide without a scale and rounding mode throws an ArithmeticException whenever the result is non-terminating, such as dividing ten thousand by three. The safe call specifies two decimal places and HALF_EVEN rounding, which is banker's rounding. HALF_EVEN matters across thousands of accounts because it does not systematically round up or down, so the averages do not drift.
In a banking project at National Bank of India, the interest-tier batch job averaged each customer's three most recent monthly closing balances to decide their tier. The AverageBalanceService added the three BigDecimal balances exactly and divided by three with scale two and HALF_EVEN rounding. For balances of ten, twenty, and thirty thousand, the average was cleanly twenty thousand, and even when a customer's balances produced a repeating decimal the result was always well defined.
Remember one sentence: averaging money is really a division problem, and division of money is never safe unless you state the scale and the rounding mode explicitly.
---