How does rounding work in NumberFormat/DecimalFormat, and what is the default rounding behavior?
answer
- Default rounding = HALF_EVEN (banker's), not HALF_UP
- HALF_EVEN: 2.5->2, 3.5->4 (ties to even)
- setRoundingMode to override
- doubles can't represent decimals exactly -> fake ties
- use BigDecimal for money
basics
~20 sWhen a number has more decimals than the format shows, DecimalFormat rounds it. By default it uses HALF_EVEN (banker's rounding), so 2.5 rounds to 2 and 3.5 rounds to 4. You can change this with setRoundingMode, e.g. to HALF_UP.
solid answer
~40 sDecimalFormat truncates the displayed value to its maximum fraction digits by rounding, and the default RoundingMode is HALF_EVEN, also called banker's rounding. HALF_EVEN rounds a tie (exactly .5) toward the nearest even digit, so 0.125 with two decimals becomes 0.12 and 0.135 becomes 0.14; this reduces cumulative bias over many roundings. This surprises people who expect HALF_UP (always round .5 up), the rule taught in school. You can override it with df.setRoundingMode(RoundingMode.HALF_UP). A second subtlety: when you format a double, binary floating-point can't represent many decimals exactly, so 0.125 might really be slightly less, changing the tie outcome. For money, format a BigDecimal (or scale beforehand) and set an explicit rounding mode rather than trusting the double-plus-default-HALF_EVEN combination. Rounding affects only the displayed string, not the underlying value.
code
java · 14 linesDecimalFormat df = new DecimalFormat("0.00");
// Default HALF_EVEN (banker's rounding):
df.format(2.345); // "2.35" or "2.34" depending on the double's true value
DecimalFormat one = new DecimalFormat("0");
one.format(2.5); // "2" <-- HALF_EVEN rounds the tie to even
one.format(3.5); // "4"
one.setRoundingMode(RoundingMode.HALF_UP);
one.format(2.5); // "3" <-- now conventional rounding
// Money done right: exact decimal + explicit mode
BigDecimal price = new BigDecimal("19.345");
df.setRoundingMode(RoundingMode.HALF_UP);
df.format(price); // "19.35"go deeper
Knows that numbers with extra decimals get rounded when formatted.
Knows you can set a RoundingMode and that .5 cases are the tricky ones.
Knows the default is HALF_EVEN, can contrast it with HALF_UP, and explains the double-precision tie hazard plus the BigDecimal fix.
Sets a rounding/decimal-precision policy for money across the system, mandates BigDecimal + explicit modes, and reviews for accumulation bias and audit/regulatory correctness.
## Why rounding happens at all A format like `"0.00"` shows two decimal places. If you hand it `3.14159`, it must decide what to display. It **rounds** the value to the format's *maximum fraction digits*. Rounding is choosing the nearest representable shorter number; the only hard case is a **tie** — a value exactly halfway, like `2.5` rounding to a whole number. ## RoundingMode — the tie-breaking rule `java.math.RoundingMode` is an enum of strategies. The ones that matter: - **HALF_UP** — ties go away from zero. 2.5 -> 3, -2.5 -> -3. This is the "round half up" everyone learns in school. - **HALF_DOWN** — ties go toward zero. 2.5 -> 2. - **HALF_EVEN** — ties go to the nearest *even* last digit. 2.5 -> 2, 3.5 -> 4, 0.125 -> 0.12, 0.135 -> 0.14. Also called **banker's rounding** because, averaged over many values, it doesn't systematically push results up, avoiding cumulative upward bias. - **UP / DOWN / CEILING / FLOOR** — always round away/toward zero / toward +infinity / toward -infinity (no tie concept). - **UNNECESSARY** — asserts no rounding is needed; throws if it is. ## The default that surprises people `DecimalFormat`'s default rounding mode is **HALF_EVEN**, not HALF_UP. So out of the box: ```java DecimalFormat df = new DecimalFormat("0"); df.format(2.5); // "2" (not "3"!) df.format(3.5); // "4" ``` If you need conventional rounding, set it: ```java df.setRoundingMode(RoundingMode.HALF_UP); df.format(2.5); // "3" ``` ## The floating-point trap on top of rounding A `double` is binary floating point and cannot store most decimal fractions exactly. `0.125` happens to be exact (it's 1/8), but `0.145` is stored as something like `0.144999999...`. So when you format a double you are rounding *that slightly-off value*, and a number you believe is a perfect tie may not be one — making the rounding mode appear to misbehave. The result is that doubles + default rounding produce results that look wrong for money. ## The correct approach for money 1. Use `BigDecimal` for monetary values (exact decimal arithmetic). 2. Pass the BigDecimal to `format` (DecimalFormat accepts it) or scale it with `setScale(2, RoundingMode.HALF_UP)` first. 3. Set an **explicit** RoundingMode on the formatter so behavior is intentional, not inherited. ## What rounding does and doesn't change Rounding affects only the **output string**. The numeric value you passed in is untouched; nothing is mutated. If you need the rounded *value* for further math, round the BigDecimal itself, don't rely on the formatter. ## Key takeaways 1. Default rounding is **HALF_EVEN** (banker's), not HALF_UP. 2. Override via `setRoundingMode(...)` when you need a specific policy. 3. Doubles can't represent decimals exactly, so apparent ties may not be ties — use BigDecimal for money. 4. Rounding changes only the displayed text, not the underlying number.
- Why is HALF_EVEN the default rather than HALF_UP?HALF_EVEN avoids the systematic upward bias HALF_UP introduces when rounding many values, since ties go to the nearest even digit and even/odd targets balance out. It's preferred in financial/statistical aggregation.
- Why can formatting a double give a rounding result you didn't expect?A double is binary and can't store most decimal fractions exactly, so a value you think is exactly .5 may be slightly above or below, changing which way the tie rounds. Use BigDecimal to avoid this.
saying these in an interview costs you the question
- Assuming the default is HALF_UP / round-half-up
- Formatting monetary doubles and trusting the displayed cents
- Thinking rounding mutates the input value
- Ignoring that floating-point error can flip a tie