What is MathContext, how does it differ from setScale, and what do DECIMAL64 and UNLIMITED mean?
answer
- MathContext = (precision = significant digits) + RoundingMode
- setScale = decimal places; MathContext = significant figures
- DECIMAL32/64/128 = 7/16/34 digits, HALF_EVEN
- UNLIMITED = precision 0 = exact, no rounding
- UNLIMITED divide of 1/3 throws
basics
~20 sMathContext bundles a precision (total significant digits) plus a RoundingMode, and you pass it to operations to round by significant figures. setScale instead fixes the number of digits after the decimal point. DECIMAL64 is a 16-digit preset; UNLIMITED means no rounding (exact).
solid answer
~50 sA MathContext is a small immutable object holding two things: a precision (the number of significant digits to keep) and a RoundingMode. You pass it to constructors and arithmetic methods (add, multiply, divide, pow…) to round the result to that many significant figures. The key contrast with setScale: setScale controls scale — digits after the decimal point — whereas MathContext controls precision — total significant digits, independent of where the point falls. So setScale(2) makes 123.456 → 123.46, while new MathContext(2) rounding makes it → 1.2E+2. Java ships presets mirroring IEEE-754 decimal formats: DECIMAL32 (7 digits), DECIMAL64 (16 digits), DECIMAL128 (34 digits), each with HALF_EVEN. MathContext.UNLIMITED means precision 0 = unlimited, i.e. exact arithmetic with no rounding — which is why an UNLIMITED (or no-context) divide of a non-terminating quotient throws. Use MathContext for significant-figure/scientific work and to cap precision in long computations; use setScale for fixed-decimal-place money.
code
java · 7 linesBigDecimal x = new BigDecimal("123.456");
System.out.println(x.round(new MathContext(2))); // 1.2E+2 (2 sig figs)
System.out.println(x.setScale(2, RoundingMode.HALF_UP)); // 123.46 (2 decimal places)
System.out.println(MathContext.DECIMAL64.getPrecision()); // 16
System.out.println(BigDecimal.ONE.divide(new BigDecimal(3), new MathContext(5))); // 0.33333
// BigDecimal.ONE.divide(new BigDecimal(3), MathContext.UNLIMITED); // throws ArithmeticExceptiongo deeper
Aware that MathContext combines a precision and a rounding mode and that there are presets like DECIMAL64.
Can use MathContext on operations and explains it rounds to significant digits, distinct from setScale's decimal places.
Chooses MathContext vs setScale by use case, knows the DECIMAL32/64/128 digit counts and that UNLIMITED is exact and can throw on divide.
Defines a domain-wide precision/rounding policy via shared MathContext constants, reasons about precision growth and performance in long pipelines, and aligns choices with IEEE-754 and storage formats.
## Two different notions: scale vs precision Recall: **scale** = digits after the decimal point; **precision** = total count of significant digits (the digits of the unscaled value). `123.456` has scale 3 and precision 6. These are the two ways to say 'how big' a number's representation is, and BigDecimal lets you control either. ## setScale — control the decimal places `setScale(newScale, roundingMode)` forces a specific number of digits **after the decimal point**, rounding as needed. This is what money wants: 'always exactly 2 decimal places'. `123.456.setScale(2, HALF_UP)` → `123.46`. ## MathContext — control the significant digits `java.math.MathContext` is an immutable pair of **`precision`** (how many significant digits to keep) and a **`RoundingMode`**. You hand it to arithmetic methods to round the *result* to that many significant figures regardless of where the decimal point lands: ```java MathContext mc = new MathContext(4, RoundingMode.HALF_UP); new BigDecimal("123.456").round(mc); // 123.5 (4 sig figs) new BigDecimal("0.00123456").round(mc); // 0.001235 (4 sig figs) new BigDecimal("123456").round(mc); // 1.235E+5 (4 sig figs) ``` Notice the number of *decimal places* varies wildly while the number of *significant digits* stays at 4. That's the essential difference from setScale. Most arithmetic methods have a MathContext overload — `add(b, mc)`, `multiply(b, mc)`, `divide(b, mc)`, `pow(n, mc)` — which is how you keep precision bounded through a long chain of operations (otherwise multiply keeps growing the scale unboundedly). ## The presets (IEEE-754 decimal formats) Java provides ready-made contexts mirroring the IEEE 754-2008 decimal types, all using **HALF_EVEN**: - **`MathContext.DECIMAL32`** — precision **7**. - **`MathContext.DECIMAL64`** — precision **16** (a common general-purpose choice; ~the significand of a double). - **`MathContext.DECIMAL128`** — precision **34** (high precision). ## UNLIMITED **`MathContext.UNLIMITED`** has precision 0, which is the sentinel for **unlimited precision** — arithmetic is performed **exactly with no rounding**. This is the default behavior when you call an operation with no MathContext. Crucially, **exact arithmetic can't represent a non-terminating quotient**, so `divide` under UNLIMITED (or the no-context overload) throws `ArithmeticException` for results like 1/3. You must switch to a bounded precision or use the scale+RoundingMode divide overload. ## When to use which - **Money / fixed decimal places** → `setScale(n, mode)`. Self-documenting and matches currency rules. - **Scientific / significant-figure work, or capping precision in long chains** → `MathContext`. - **Need exact and know it terminates** → UNLIMITED / no context (but guard divide). MathContext is itself immutable and cheap, so it's fine to declare shared constants for your domain's standard precision/rounding policy and pass them everywhere for consistency.
- Round 123.456 with new MathContext(2) vs setScale(2). What's the difference?MathContext(2) keeps 2 significant digits → 1.2E+2 (i.e. 120). setScale(2) keeps 2 decimal places → 123.46. One counts significant figures, the other counts digits after the point.
- Why does divide throw even with MathContext.UNLIMITED?UNLIMITED means exact, no rounding. A non-terminating quotient like 1/3 has no finite exact representation, so there's nothing valid to return — it throws ArithmeticException. Use a bounded precision or scale+RoundingMode.
- What precision does DECIMAL64 use and why is it a common default?16 significant digits with HALF_EVEN — roughly matching a double's significand, giving a sensible general-purpose precision without unbounded growth.
saying these in an interview costs you the question
- Saying MathContext precision means decimal places (it's significant digits)
- Thinking UNLIMITED means 'maximum precision number' rather than exact/no-rounding
- Believing setScale and MathContext are interchangeable
- Assuming MathContext divide never throws (UNLIMITED still does on non-terminating results)