Lodash _.round() method
What you’ll learn
- How
_.roundwrapsMath.roundvia thecreateRoundfactory—positive precision means decimals, negative precision means tens / hundreds / thousands. - That half-rounding is asymmetric: ties round toward
+Infinity, so_.round(-0.5)is-0, not-1. It’s not banker’s rounding. - How the exponential-shift trick fixes the famous
Math.round(1.005 * 100) / 100 = 1bug (Lodash returns1.01). - How inputs are coerced:
null → 0,undefined → NaN,"5.5" → 5.5;Infinitypasses through; BigInt throws. - When to reach for
_.roundvsNumber.prototype.toFixed,Intl.NumberFormat, or scale-then-integer-math.
Prerequisites
Read _.ceil() or _.floor() first—all three share the createRound factory, differing only in the Math method they wrap.
- Native
Math.roundbehaviour: half-toward-positive-infinity.Math.round(-0.5)is-0, not-1. - Scientific notation: JavaScript stores numbers as
mantissa × 10^exponent; bumping the exponent shifts the decimal point safely.
Overview
The factory createRound('round') returns a function that runs the input through toNumber, clamps the precision via nativeMin(toInteger(precision), 292), then splits the number's scientific form on 'e', bumps the exponent by precision, calls Math.round, and shifts back. The exponent-trick avoids the multiply-divide precision loss that plagues naive rounding.
Half toward +∞
_.round(0.5) = 1; _.round(-0.5) = -0 (not -1). Inherited from Math.round.
Positive / negative precision
Positive → decimals; negative → tens, hundreds, thousands.
Exponent-shift fix
Avoids the classic Math.round(1.005 * 100) / 100 = 1 bug by routing through string × exponent.
Syntax
_.round(number, [precision = 0]) - number: the value to round. Coerced via
toNumber: strings parse,null→0,undefined→NaN. - precision (optional, default
0): integer. Positive = decimal places; negative = round to tens / hundreds. Clamped at292internally. - Returns:
number—the rounded value.NaN→NaN;±Infinitypasses through.
Basics — positive and negative precision
Positive precision rounds to n decimal places. Negative precision rounds to the nearest 10|n|. The official docs example covers all three modes.
import round from "lodash/round";
console.log(round(4.006)); // 4 (docs example)
console.log(round(4.006, 2)); // 4.01 (docs example)
console.log(round(4060, -2)); // 4100 (docs example)
// Common usage
console.log(round(5.678, 1)); // 5.7
console.log(round(3.14159265358979, 5)); // 3.14159
console.log(round(1234.56789, 2)); // 1234.57
// Negative precision = round to nearest 10^|n|
console.log(round(9876543210.123, -5)); // 9876500000 (NOT 9876543210)
console.log(round(1234, -3)); // 1000 Half-rounding is asymmetric
Ties don’t go to the nearest even number (that’s “banker’s rounding”) and they don’t go away from zero either. They go toward positive infinity. So positive halves round up, but negative halves round toward zero.
import round from "lodash/round";
// Positive halves: round up
console.log(round(0.5)); // 1
console.log(round(1.5)); // 2
console.log(round(2.5)); // 3 (NOT 2 like banker's rounding would give)
// Negative halves: round toward zero
console.log(round(-0.5)); // -0 (NOT -1)
console.log(round(-1.5)); // -1
console.log(round(-2.5)); // -2 (NOT -3 like "round half away from zero")
// Mid-decimal half
console.log(round(2.345, 2)); // 2.35 Why _.round beats Math.round on precision
1.005 isn’t really stored as 1.005—it’s 1.00499999999999989…. So Math.round(1.005 * 100) / 100 rounds down to 1. Lodash sidesteps the multiply-divide round-trip by shifting the exponent in the string form, which is why _.round(1.005, 2) correctly returns 1.01.
import round from "lodash/round";
// Native naive rounding loses the half
console.log(Math.round(1.005 * 100) / 100); // 1 (BUG)
console.log(round(1.005, 2)); // 1.01 (correct)
// _.round handles 0.1 + 0.2 cleanly at sensible precisions
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(round(0.1 + 0.2, 1)); // 0.3
// Input coercion (toNumber)
console.log(round(undefined)); // NaN
console.log(round(null)); // 0
console.log(round("5.5")); // 6
console.log(round(Infinity, 2)); // Infinity
console.log(round(NaN, 2)); // NaN
// BigInt throws
try {
console.log(round(1n, 2));
} catch (err) {
console.log("Threw:", err.message); // "Cannot convert a BigInt value to a number"
} 📋 _.round vs Math.round vs toFixed
| Input | _.round(n, p) | Math.round(n * 10^p) / 10^p | n.toFixed(p) | Notes |
|---|---|---|---|---|
1.005, 2 | 1.01 | 1 | "1.00" | Only Lodash gets the “intuitive” answer; others suffer FP precision loss. |
2.5, 0 | 3 | 3 | "3" | All three round halves toward +∞ in Node; toFixed may differ across browsers. |
-0.5, 0 | -0 | -0 | "0" | -0.5 rounds to -0, not -1. |
4060, -2 | 4100 | 4100 | error | toFixed doesn’t support negative digits. |
| Return type | number | number | string | Use toFixed when you want trailing zeros preserved for display. |
Pitfalls to avoid
Not banker’s, not symmetric
If you expected _.round(2.5) to be 2 (banker’s) or _.round(-2.5) to be -3 (away-from-zero), you’ll be surprised. It’s “half toward +∞” in both cases.
Reads “round to the nearest 10|n|”
_.round(9876543210, -5) is 9876500000, not 9876543210. The reference page for this function in many tutorials gets this wrong—verify empirically.
Still IEEE-754
The exponent-shift fixes 1.005-style traps but not every floating-point quirk. For money, scale to cents and do integer math, or use a decimal library.
Returns a number
_.round(1.20, 2) is 1.2 (the trailing zero is lost). If you need "1.20" for display, use .toFixed(2) on the rounded value or use Intl.NumberFormat.
Throws
BigInt is already an integer; rounding doesn’t apply. Convert with Number(bigint) first if you really need the round trip.
❓ FAQ
Summary
- Purpose: round to a configurable precision; thin wrapper over
Math.roundwith an exponent-shift to dodge float quirks. - Remember: half-toward-
+∞(not banker’s), positive precision = decimals, negative = tens / hundreds;null→0,undefined→NaN; BigInt throws. - Next: Lodash _.subtract(), or read the official Lodash docs for _.round.
_.round(1.005, 2) returns 1.01, but naive Math.round(1.005 * 100) / 100 returns 1—the famous JavaScript rounding bug. _.round sidesteps it by stringifying the number and shifting its exponent ('1.005e0' → '1.005e2') before calling Math.round.
6 people found this page helpful
