JavaScript example

How to round to 2 decimal places in JavaScript

7 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

To get a number rounded to at most two decimals, use Math.round(x * 100) / 100: 3.14159 becomes 3.14 and 10 stays 10. To display exactly two decimals, use x.toFixed(2), which returns the string "5.00". Both trip over values like 1.005, which round down to 1 because of how floating point stores them.

JavaScript has no round(x, 2). Math.round only rounds to a whole number, so every two-decimal recipe either scales the number up and back down, or turns it into a string. The first question to answer is which one you need: a number you keep calculating with, or text for a price tag or a table cell. The second is how much you care about the handful of inputs, like 1.005, that binary floating point can't store exactly. Each example runs on this page: hit Run, then edit the code and run it again.

1Math.round(x * 100) / 100Recommended

Multiply by 100 to move the two decimals you want to keep in front of the point, round to the nearest whole number, then divide by 100 to move them back. The result is still a number, and it only has as many decimals as it needs: this is the "at most 2 decimal places, if necessary" behaviour.

round.js

Output

Prints 3.14, 2, 2.5, 10 and number, then 3.142 1234.6 1. Note that 1.999 comes back as 2, not 2.00: a number has no idea how many decimals you meant to show. If you need the padding, that's a formatting job for toFixed or Intl.NumberFormat below.

2The 1.005 trap and Number.EPSILON

JavaScript numbers are 64-bit binary floats, and most decimal fractions have no exact binary form. 1.005 is really stored as a hair below 1.005, so 1.005 * 100 lands just under 100.5 and Math.round correctly rounds it down. The widely copied fix adds Number.EPSILON (about 2.2e-16) before scaling. It rescues 1.005, but the error it has to beat grows with the size of the number, so it quietly fails on others.

trap.js

Output

The naive version prints 1, and the next two lines show why: 100.49999999999999 and 1.0049999999999998934. The Number.EPSILON nudge gives 1.01, but still prints 10.07 4.97 where you wanted 10.08 4.98. The toPrecision(15) helper gets all three right (1.01 10.08 4.98, then 1.235 2.5): a double holds 15 to 17 significant digits, so cutting to 15 removes the representation error without touching real data. One more catch: Math.round rounds halves toward positive infinity, so roundTo(-1.005) is -1. Round the absolute value and put the sign back, as roundHalfAway does, to get -1.01 1.01. For money, the real fix is to store whole cents as integers and never round a float at all.

3toFixed(2): always show two decimals

Number.prototype.toFixed(digits) rounds and pads in one step, so 5 becomes "5.00". That is exactly what a price or a report column wants. The two things to remember: it returns a string, and it rounds the stored binary value, so it has its own version of the 1.005 problem.

to-fixed.js

Output

Prints 5.00, 1.50 3.14 and string 19.90. Then comes the classic bug: total + 1 is 19.901, because + with a string concatenates. Convert back with Number() to get 20.9, and notice that +(2.5).toFixed(2) is plain 2.5 again: padding only exists in strings. The quirk line prints 1.00 2.67 8.35. 1.005 and 2.675 are stored slightly low and round down, while 8.345 is stored slightly high and rounds up. Finally, 3 -3 -2: toFixed rounds -2.5 away from zero while Math.round rounds it up to -2. Call it on a literal as (5).toFixed(2), since 5.toFixed(2) is a syntax error.

4Intl.NumberFormat for display

Intl.NumberFormat is the built-in formatter behind toLocaleString, and it separates the two questions cleanly. maximumFractionDigits: 2 means "at most two", and adding minimumFractionDigits: 2 means "always two". It rounds from the shortest decimal form of the number, the same digits console.log prints, so 1.005 rounds to 1.01 the way a person expects. It also handles locale separators and lets you pick the rounding rule.

intl.js

Output

Prints 3.14 2.5 10, then 5.00 2.50 1.01: compare that last value with toFixed's 1.00. 1234.5 comes out as 1,234.50 in en-US and 1.234,50 in de-DE, so the output is for people, not for Number() to read back. The rounding modes give 2.35 -2.35 (halfExpand, ties away from zero), 2.34 -2.34 (halfEven, the banker's rule), 2.34 -2.34 (trunc) and 2.35 -2.34 (ceil). The last line prints 0.13. Create the formatter once and reuse it in loops; constructing one is far slower than calling format.

5Float to a whole number: trunc, floor, ceil, round

To drop the decimals entirely, pick one of four Math functions. They agree on positive numbers most of the time and disagree on negatives and on halves. Math.trunc cuts the fraction off (toward zero), Math.floor goes down, Math.ceil goes up, and Math.round goes to the nearest, with halves going up.

whole.js

Output

The table shows the split: -3.7 truncates to -3 but floors to -4, and -2.5 rounds to -2. ~~x and x | 0 truncate too (3 3 -3), but they squeeze the value through a 32-bit integer first, so 3_000_000_000.5 | 0 prints -1294967296 where Math.trunc gives 3000000000. parseInt is for strings: handed a number, it converts it to text first, and 0.0000005 becomes "5e-7", so the result is 5. On real strings it stops at the first non-digit, which is why parseInt('42.9px') is 42. Use Math.trunc for numbers.

6Which should you use?

MethodReturns1.005 givesBest for
Math.round(x * 100) / 100number1 (wrong)Quick rounding where exact ties don’t matter
Math.round((x + Number.EPSILON) * 100) / 100number1.01, but 10.075 → 10.07Small numbers only
roundTo(x, 2) with toPrecision(15)number1.01Rounding values you keep calculating with
x.toFixed(2)string"1.00"Always two decimals, no locale
Intl.NumberFormatstring"1.01"Anything a person reads
Math.trunc / floor / ceil / roundnumber1Whole numbers

Frequently asked questions

Why does Math.round(1.005 * 100) / 100 give 1 instead of 1.01?

Because 1.005 cannot be stored exactly as a binary float. The nearest double is 1.0049999999999998934, so 1.005 * 100 is 100.49999999999999 and Math.round correctly rounds that down to 100. Trim the scaled value first with Math.round(Number((x * 100).toPrecision(15))) / 100, which gives 1.01, or format with Intl.NumberFormat.

Does adding Number.EPSILON fix rounding in JavaScript?

Only for small numbers. Number.EPSILON is about 2.2e-16, the gap between 1 and the next double, but the representation error grows with the size of the value. Math.round((1.005 + Number.EPSILON) * 100) / 100 gives 1.01, yet the same formula returns 10.07 for 10.075 and 4.97 for 4.975.

How do I always show 2 decimal places, even for whole numbers?

Use x.toFixed(2), which turns 5 into "5.00", or new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(x), which also adds thousands separators. Both return strings. Converting the result back with Number() drops the trailing zeros, because numbers do not store formatting.

Why does toFixed return a string?

Because its job is formatting: padding like the zeros in "5.00" only exists in text. The catch is that + with a string concatenates, so (19.9).toFixed(2) + 1 is "19.901". Keep numbers as numbers while you calculate and call toFixed only when you display the result, or wrap it in Number() if you really need a number back.

What is the difference between Math.trunc and Math.floor?

They only differ for negative numbers. Math.trunc removes the fractional part, rounding toward zero, so Math.trunc(-3.7) is -3. Math.floor always rounds down, so Math.floor(-3.7) is -4. For positive numbers both give the same result.

Is ~~x or x | 0 faster than Math.trunc for converting a float to an int?

It makes no measurable difference in modern engines, and the bitwise versions are only correct inside the 32-bit integer range: 3_000_000_000.5 | 0 is -1294967296. Math.trunc works on every number and says what it does, so use it.

Run it yourself

Open any of these in the full JavaScript editor to tweak, run and share.

JavaScript playground