SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: Math.round(-2.5) is -2: ties round towards positive infinity, not away from zero

· by Serguey Asael Shinder / Serguey Shinder

Most people learn "round half away from zero" at school: 2.5 becomes 3 and -2.5 becomes -3. Java's Math.round does something else:

Math.round(2.5)    // 3
Math.round(-2.5)   // -2
Math.round(-2.51)  // -3
Math.round(-0.5)   // 0

Why. The javadoc for Math.round(double)) says it returns the closest long to the argument, "with ties rounding to positive infinity". A tie is a value exactly halfway between two integers. Positive ties move up, away from zero; negative ties also move up, which for a negative number means towards zero. So the result is not symmetric around zero: Math.round(-x) is not always -Math.round(x).

Where it matters. Anywhere the sign is meaningful and halves are common: refunds and reversals stored as negative amounts, temperature or sensor readings around zero, differences between two measurements. A reversal of a charge that rounded up to 3 can round to -2, and the two no longer cancel.

Serguey Asael Shinder: Math.round(-2.5) is -2: ties round towards positive infinity, not away from zero
Math.round(-2.5) is -2: ties round towards positive infinity, not away from zero — Serguey Asael Shinder

What to use instead. Decide the rule your domain needs and name it. BigDecimal makes the rule explicit through RoundingMode:

new BigDecimal("-2.5").setScale(0, RoundingMode.HALF_UP);    // -3, half away from zero
new BigDecimal("-2.5").setScale(0, RoundingMode.HALF_EVEN);  // -2, banker's rounding
new BigDecimal("2.5").setScale(0, RoundingMode.HALF_EVEN);   //  2

Despite its name, RoundingMode.HALF_UP rounds ties away from zero, which is the symmetric behaviour most people expect. HALF_EVEN rounds ties to the even neighbour, which avoids a systematic upward bias when many values are summed.

And a related trap. If the value starts as a double, the "half" may not be a half at all. 2.675 cannot be represented exactly in binary, so rounding it to two decimals can surprise you regardless of the mode. Build the BigDecimal from the string or from the original decimal source, not from the double.

The rule. Math.round is fine for display and for non-negative values. For money, signed quantities or anything that has to round the same way in another system, write the rounding mode down in code.

No JDK runs on the machine this note was written on; the behaviour described is the one specified in the linked javadoc.