javascript - How to round to at most 2 decimal places, if necessary?

ID : 138

viewed : 24

Tags : javascriptroundingdecimal-pointjavascript

Top 5 Answer for javascript - How to round to at most 2 decimal places, if necessary?

vote vote

99

Use Math.round() :

Math.round(num * 100) / 100 

Or to be more specific and to ensure things like 1.005 round correctly, use Number.EPSILON :

Math.round((num + Number.EPSILON) * 100) / 100 
vote vote

90

If the value is a text type:

parseFloat("123.456").toFixed(2); 

If the value is a number:

var numb = 123.23454; numb = numb.toFixed(2); 

There is a downside that values like 1.5 will give "1.50" as the output. A fix suggested by @minitech:

var numb = 1.5; numb = +numb.toFixed(2); // Note the plus sign that drops any "extra" zeroes at the end. // It changes the result (which is a string) into a number again (think "0 + foo"), // which means that it uses only as many digits as necessary. 

It seems like Math.round is a better solution. But it is not! In some cases it will NOT round correctly:

Math.round(1.005 * 100)/100 // Returns 1 instead of expected 1.01! 

toFixed() will also NOT round correctly in some cases (tested in Chrome v.55.0.2883.87)!

Examples:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56. parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56. // However, it will return correct result if you round 1.5551. parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.  1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356. // However, it will return correct result if you round 1.35551. 1.35551.toFixed(2); // Returns 1.36 as expected. 

I guess, this is because 1.555 is actually something like float 1.55499994 behind the scenes.

Solution 1 is to use a script with required rounding algorithm, for example:

function roundNumber(num, scale) {   if(!("" + num).includes("e")) {     return +(Math.round(num + "e+" + scale)  + "e-" + scale);   } else {     var arr = ("" + num).split("e");     var sig = ""     if(+arr[1] + scale > 0) {       sig = "+";     }     return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);   } } 

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

NOTE: This is not a universal solution for everyone. There are several different rounding algorithms, your implementation can be different, depends on your requirements. https://en.wikipedia.org/wiki/Rounding

Solution 2 is to avoid front end calculations and pull rounded values from the backend server.

Edit: Another possible solution, which is not a bullet proof also.

Math.round((num + Number.EPSILON) * 100) / 100 

In some cases, when you round number like 1.3549999999999998 it will return incorrect result. Should be 1.35 but result is 1.36.

vote vote

77

You can use

function roundToTwo(num) {         return +(Math.round(num + "e+2")  + "e-2"); } 

I found this over on MDN. Their way avoids the problem with 1.005 that was mentioned.

roundToTwo(1.005) 1.01 roundToTwo(10) 10 roundToTwo(1.7777777) 1.78 roundToTwo(9.1) 9.1 roundToTwo(1234.5678) 1234.57 
vote vote

70

MarkG's answer is the correct one. Here's a generic extension for any number of decimal places.

Number.prototype.round = function(places) {   return +(Math.round(this + "e+" + places)  + "e-" + places); } 

Usage:

var n = 1.7777;     n.round(2); // 1.78 

Unit test:

it.only('should round floats to 2 places', function() {    var cases = [     { n: 10,      e: 10,    p:2 },     { n: 1.7777,  e: 1.78,  p:2 },     { n: 1.005,   e: 1.01,  p:2 },     { n: 1.005,   e: 1,     p:0 },     { n: 1.77777, e: 1.8,   p:1 }   ]    cases.forEach(function(testCase) {     var r = testCase.n.round(testCase.p);     assert.equal(r, testCase.e, 'didn\'t get right number');   }); }) 
vote vote

55

You should use:

Math.round( num * 100 + Number.EPSILON ) / 100 

No one seems to be aware of Number.EPSILON.

Also it's worth noting that this is not a JavaScript weirdness like some people stated.

That is simply the way floating point numbers works in a computer. Like 99% of programming languages, JavaScript doesn't have home made floating point numbers; it relies on the CPU/FPU for that. A computer uses binary, and in binary, there isn't any numbers like 0.1, but a mere binary approximation for that. Why? For the same reason than 1/3 cannot be written in decimal: its value is 0.33333333... with an infinity of threes.

Here come Number.EPSILON. That number is the difference between 1 and the next number existing in the double precision floating point numbers. That's it: There is no number between 1 and 1 + Number.EPSILON.

EDIT:

As asked in the comments, let's clarify one thing: adding Number.EPSILON is relevant only when the value to round is the result of an arithmetic operation, as it can swallow some floating point error delta.

It's not useful when the value comes from a direct source (e.g.: literal, user input or sensor).

EDIT (2019):

Like @maganap and some peoples have pointed out, it's best to add Number.EPSILON before multiplying:

Math.round( ( num + Number.EPSILON ) * 100 ) / 100 

EDIT (december 2019):

Lately, I use a function similar to this one for comparing numbers epsilon-aware:

const ESPILON_RATE = 1 + Number.EPSILON ; const ESPILON_ZERO = Number.MIN_VALUE ;  function epsilonEquals( a , b ) {   if ( Number.isNaN( a ) || Number.isNaN( b ) ) {     return false ;   }   if ( a === 0 || b === 0 ) {     return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;   }   return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ; } 

My use-case is an assertion + data validation lib I'm developing for many years.

In fact, in the code I'm using ESPILON_RATE = 1 + 4 * Number.EPSILON and EPSILON_ZERO = 4 * Number.MIN_VALUE (four times the epsilon), because I want an equality checker loose enough for cumulating floating point error.

So far, it looks perfect for me. I hope it will help.

Top 3 video Explaining javascript - How to round to at most 2 decimal places, if necessary?

Related QUESTION?