BigDecimal.setScale with RoundingMode.UNNECESSARY succeeds only when the requested scale can represent the value exactly.
Java RoundingMode.UNNECESSARY: reject hidden fractional cents
Validate a money input before posting
A two-decimal currency field can accept 47.00 but should reject 47.005 if the API forbids rounding at ingestion. UNNECESSARY turns that rule into an exception instead of silently moving value. Apply it before persisting or publishing a ledger entry.
Division may need an explicit rounding policy when shares are calculated. In contrast, validation of an externally supplied amount can require exact two-decimal representation. These are different boundaries.
Keep representation policy deliberate
Scale is part of BigDecimal.equals. After validation, normalize the stored scale if the database or equality policy expects one representation. Scale equality explains why equal numeric values can still behave differently as keys.
Working program
import java.math.BigDecimal;
import java.math.RoundingMode;
public class LedgerAmountScaleGate {
static BigDecimal cents(String input) {
return new BigDecimal(input).setScale(2, RoundingMode.UNNECESSARY);
}
public static void main(String[] args) {
System.out.println(cents("47.00"));
try { cents("47.005"); }
catch (ArithmeticException rejected) { System.out.println("fractional cent rejected"); }
}
}Output
47.00
fractional cent rejectedCost and ownership
Parsing and scaling allocate BigDecimal values proportional to the input's digits. Cap input length before parsing untrusted text. An exception is a validation signal here; it should be translated into a field-level error at the request boundary.
Common Mistakes
- Do not round an input that the contract requires to be exact.
- Do not treat scale validation as a substitute for input-length limits.
- Do not use BigDecimal.equals as numeric equality without considering scale.
Read next
Java BigDecimal: decimal amounts and explicit rounding, Java BigDecimal.divide: make nonterminating quotients explicit, Java BigDecimal equality: value and scale are separate contracts, Java DecimalFormat: reject trailing input and pin the locale.
