Explained clearly 6 min read

Rounding in JavaScript: Math.round() Half-Up Behavior and Banker’s Rounding

JavaScript's Math.round() rounds ties toward +∞, not away from zero, so Math.round(-2.5) is -2. Banker's rounding (half-to-even) requires a custom function or decimal library.

Short Answer

JavaScript's Math.round() rounds ties toward +∞, not away from zero, so Math.round(-2.5) is -2. Banker's rounding (half-to-even) requires a custom function or decimal library.

Rule Statement: What Math.round() Actually Does

In JavaScript, Math.round(x) returns the integral Number closest to x. When x is exactly halfway between two integers, the tie is broken toward positive infinity. This is defined by ECMA-262, the JavaScript language specification, in the Math.round abstract operation. It is not the same as round half away from zero for negative values.

Math.round(x) returns the Number value that is closest to x and is integral. If two integral Numbers are equally close to x, then the result is the Number value that is closer to +∞. — ECMA-262, Math.round

Consequences: Math.round(2.5) → 3; Math.round(-2.5) → -2; Math.round(-0.5) → -0 (negative zero). For positive values, this matches common half-up rounding. For negatives, it is asymmetric and often surprises developers, scientists, and students.

Worked Examples

Integer ties and near-ties

  • Math.round(2.4) → 2. Fractional part below 0.5 rounds down.
  • Math.round(2.5) → 3. Exact tie rounds toward +∞.
  • Math.round(2.6) → 3. Fractional part above 0.5 rounds up.
  • Math.round(-2.4) → -2. Nearest integer is -2.
  • Math.round(-2.5) → -2. Tie rounds toward +∞, not away from zero.
  • Math.round(-2.6) → -3. Nearest integer is -3.

Decimal places and floating-point traps

To round to decimal places, developers often scale by a power of ten, round, then divide. This works for many values but can fail because JavaScript Numbers are IEEE 754 binary64 floating-point values. For example, 2.675 is stored slightly below the exact decimal value. The expression 2.675 * 100 produces 267.49999999999997, so Math.round(2.675 * 100) / 100 returns 2.67, not the decimal half-up result 2.68.

This is a representation problem, not a Math.round logic problem. For exact decimal semantics, use a decimal library such as decimal.js, big.js, or a string-based rounding routine.

Banker’s rounding in JavaScript

Banker’s rounding, also called round half to even, breaks ties by choosing the even neighbor. JavaScript has no built-in Math.roundHalfEven. A simple implementation for decimal places is:

  • function roundHalfEven(value, decimals = 0) {
  • const factor = 10 ** decimals;
  • const scaled = value * factor;
  • const floor = Math.floor(scaled);
  • const fraction = scaled – floor;
  • const epsilon = Number.EPSILON * Math.abs(scaled);
  • if (Math.abs(fraction – 0.5) < epsilon) {
  • return (floor % 2 === 0 ? floor : floor + 1) / factor;
  • }
  • return Math.round(scaled) / factor;
  • }

Examples: roundHalfEven(2.5) → 2; roundHalfEven(3.5) → 4; roundHalfEven(-2.5) → -2; roundHalfEven(-3.5) → -4. The epsilon guard helps with some binary representation artifacts, but exact decimal rounding still requires decimal arithmetic.

Counter-Examples: Common Misreadings

  • Misconception: Math.round always rounds 0.5 away from zero. Counter-example: Math.round(-2.5) is -2, not -3.
  • Misconception: Math.round is banker’s rounding. Counter-example: Math.round(2.5) is 3, while banker’s rounding gives 2.
  • Misconception: Scaling by 100 fixes all decimal rounding. Counter-example: 2.675 * 100 is 267.49999999999997, so rounding yields 2.67.
  • Misconception: Math.round(-0.5) is 0. Counter-example: It returns -0, which behaves like 0 in comparisons but is visible via Object.is(-0, 0) === false.
  • Misconception: Rounding to significant figures is the same as rounding to decimal places. Counter-example: 0.004567 to 2 significant figures is 0.0046, not 0.00.

Convention Comparison Table

Convention Tie rule 2.5 3.5 -2.5 -3.5
Math.round (JS) Toward +∞ 3 4 -2 -3
Half-up (away from zero) Away from zero 3 4 -3 -4
Banker’s (half-to-even) To even neighbor 2 4 -2 -4
Half-down (toward zero) Toward zero 2 3 -2 -3
Truncation Discard fraction 2 3 -2 -3

Note that Math.round is sometimes labeled half-up in casual JavaScript documentation because it rounds 0.5 up for positive numbers. Strictly, it is round-half-toward-positive-infinity. For scientific and regulatory work, the tie rule must be stated explicitly.

Standards Citation

Several standards and guides address rounding, often recommending round-half-to-even for significant-digit decisions:

  • ISO 80000-1:2009, Clause 6.4 — Quantities and units — Part 1: General. Recommends that when the digit following the last retained digit is exactly 5 with no nonzero digits after it, the retained digit should be rounded to the nearest even digit.
  • ASTM E29-13 — Standard Practice for Using Significant Digits in Test Data to Determine Conformance with Specifications. Provides a round-to-even procedure for significant digits in test data.
  • NIST SP 811 — Guide for the Use of the International System of Units (SI). Discusses rounding and significant figures; emphasizes one-step rounding and avoiding double rounding.
  • JCGM 100:2008 (GUM) — Evaluation of measurement data — Guide to the expression of uncertainty in measurement. Gives guidance on reporting results and rounding uncertainty, generally to no more than two significant digits.
  • IEEE 754-2019 — Standard for Floating-Point Arithmetic. Defines roundTiesToEven as the default rounding mode for binary floating-point arithmetic.
  • ECMA-262 — ECMAScript Language Specification, Math.round. Defines the tie-toward-positive-infinity behavior of JavaScript’s Math.round.

Because JavaScript’s Math.round follows ECMA-262 rather than ISO 80000-1 or ASTM E29, it is not the correct default for many scientific, metrology, and regulatory workflows that require banker’s rounding or explicit significant-figure rules.

Common Mistakes

  1. Assuming symmetry for negatives. Math.round(-2.5) is -2, not -3. If your data can be negative, test both signs.
  2. Using Math.round for significant figures without scaling. Significant figures depend on magnitude; Math.round works on integer distance, not significant digits.
  3. Double rounding. Rounding first to an intermediate precision and then again can change the final result. Round once, from the original value, to the final precision.
  4. Ignoring floating-point representation. Values such as 0.1, 0.2, and 2.675 are not exact binary fractions. Expect artifacts near ties.
  5. Using toFixed as a rounding function. Number.prototype.toFixed returns a string and may use a different internal rounding path; it also has representation quirks.
  6. Confusing decimal places with significant figures. 12.345 to 2 decimal places is 12.35; to 2 significant figures it is 12.

Software Behavior Note

JavaScript’s Number type is IEEE 754 binary64. The language specification defines Math.round for Number values. Other methods behave differently: Math.floor rounds toward -∞, Math.ceil rounds toward +∞, and Math.trunc discards the fractional part toward zero. None of these is banker’s rounding. Libraries such as decimal.js, big.js, and bignumber.js provide configurable rounding modes, including ROUND_HALF_EVEN. For financial and scientific applications, prefer a decimal library when exact decimal rounding is required.

In other environments: Python’s built-in round() uses round-half-to-even for binary floats; Excel’s ROUND function rounds half away from zero, while ROUND.EVEN uses banker’s rounding; SQL databases vary. Always verify the tie rule of the platform you use.

Quick Reference Table

Goal JavaScript expression Notes
Nearest integer, ties toward +∞ Math.round(x) Native ECMAScript behavior
Round down toward -∞ Math.floor(x) Not a tie rule; always down
Round up toward +∞ Math.ceil(x) Not a tie rule; always up
Truncate toward zero Math.trunc(x) Drops fractional part
Round to n decimal places Math.round(x * 10 ** n) / 10 ** n Floating-point caveats apply
Banker’s rounding Custom roundHalfEven(x, n) No native method
Exact decimal rounding decimal.js / big.js Configurable rounding modes

Sources & Further Reading

  • ECMA-262, ECMAScript Language Specification, Math.round.
  • IEEE 754-2019, IEEE Standard for Floating-Point Arithmetic.
  • ISO 80000-1:2009, Quantities and units — Part 1: General, Clause 6.4.
  • ASTM E29-13, Standard Practice for Using Significant Digits in Test Data to Determine Conformance with Specifications.
  • NIST SP 811, Guide for the Use of the International System of Units (SI).
  • JCGM 100:2008 (GUM), Evaluation of measurement data — Guide to the expression of uncertainty in measurement.

For more precision-and-rounding guidance, explore our related articles on Banker’s Rounding, Half-Up Rounding, IEEE 754 roundTiesToEven, and Significant Figures.

FAQ

Does JavaScript have a built-in banker's rounding function?

No. ECMAScript defines only Math.round, Math.floor, Math.ceil, and Math.trunc. Banker's rounding requires a custom function or a decimal library with a ROUND_HALF_EVEN mode.

Why does Math.round(-0.5) return -0?

Because Math.round breaks ties toward +∞. The two nearest integers to -0.5 are -1 and 0. Since 0 is closer to +∞, the result is 0, represented as -0 in IEEE 754. It compares equal to 0, but Object.is(-0, 0) is false.

Is Math.round the same as half-up rounding?

For positive numbers, yes: 2.5 rounds to 3. For negative numbers, no: -2.5 rounds to -2, whereas half-up away from zero would give -3. It is more precise to call it round-half-toward-positive-infinity.

How do I round to 2 decimal places in JavaScript?

A common approach is Math.round(value * 100) / 100. However, binary floating-point representation can produce unexpected results near ties, such as 2.675 * 100 = 267.49999999999997. For exact decimal rounding, use a decimal library or string-based arithmetic.

Which rounding method should I use for scientific data?

Follow the standard or guideline governing your field. ISO 80000-1 and ASTM E29 generally recommend round-half-to-even for significant-digit decisions. NIST SP 811 and the GUM provide additional guidance on significant figures and uncertainty reporting.

Verified sources

References

  1. ECMA-262, ECMAScript Language Specification, Math.round.
  2. IEEE 754-2019, IEEE Standard for Floating-Point Arithmetic.
  3. ISO 80000-1:2009, Quantities and units — Part 1: General, Clause 6.4.
  4. ASTM E29-13, Standard Practice for Using Significant Digits in Test Data to Determine Conformance with Specifications.
  5. NIST SP 811, Guide for the Use of the International System of Units (SI).
  6. JCGM 100:2008 (GUM), Evaluation of measurement data — Guide to the expression of uncertainty in measurement.

Leave a Reply

Your email address will not be published. Required fields are marked *