skip to content

What is MathContext, how does it differ from setScale, and what do DECIMAL64 and UNLIMITED mean?

level: principalimportance: should knowfreq 41%

answer

  1. MathContext = (precision = significant digits) + RoundingMode
  2. setScale = decimal places; MathContext = significant figures
  3. DECIMAL32/64/128 = 7/16/34 digits, HALF_EVEN
  4. UNLIMITED = precision 0 = exact, no rounding
  5. UNLIMITED divide of 1/3 throws

basics

~20 s

MathContext 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 s

A 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 lines
java
BigDecimal 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 ArithmeticException

go deeper

for a junior

Aware that MathContext combines a precision and a rounding mode and that there are presets like DECIMAL64.

for a middle

Can use MathContext on operations and explains it rounds to significant digits, distinct from setScale's decimal places.

for a senior

Chooses MathContext vs setScale by use case, knows the DECIMAL32/64/128 digit counts and that UNLIMITED is exact and can throw on divide.

for a principal

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)

context