Formatting a number with exactly two decimals in JavaScript

Formatting a number with exactly two decimals in JavaScript

I have this line of code which rounds my numbers to two decimal places. But I get numbers like this: 10.8, 2.4, etc. These are not my idea of two decimal places so how I can improve the following?
Math.round(price*Math.pow(10,2))/Math.pow(10,2);

I want numbers like 10.80, 2.40, etc. Use of jQuery is fine with me.

Solutions/Answers:

Solution 1:

To format a number using fixed-point notation, you can simply use the toFixed method:

(10.8).toFixed(2); // "10.80"

var num = 2.4;
alert(num.toFixed(2)); // "2.40"

Note that toFixed() returns a string.

IMPORTANT: Note that toFixed does not actually round, 90% of the time, it will return the rounded value but for many cases it doesn’t actually work. Try this in your console:

2.005.toFixed(2)

You will get the wrong answer

There is no natural way of getting a decimal rounding in javascript, you will need your own polyfill or use a library. You can look at mozilla’s polyfill for this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round

Solution 2:

This is an old topic but still top-ranked Google results and the solutions offered share the same floating point decimals issue. Here is the (very generic) function I use, thanks to MDN:

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}

As we can see, we don’t get these issues:

round(1.275, 2);   // Returns 1.28
round(1.27499, 2); // Returns 1.27

This genericity also provides some cool stuff:

round(1234.5678, -2);   // Returns 1200
round(1.2345678e+2, 2); // Returns 123.46
round("123.45");        // Returns 123

Now, to answer the OP’s question, one has to type:

round(10.8034, 2).toFixed(2); // Returns "10.80"
round(10.8, 2).toFixed(2);    // Returns "10.80"

Or, for a more concise, less generic function:

function round2Fixed(value) {
  value = +value;

  if (isNaN(value))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + 2) : 2)));

  // Shift back
  value = value.toString().split('e');
  return (+(value[0] + 'e' + (value[1] ? (+value[1] - 2) : -2))).toFixed(2);
}

You can call it with:

round2Fixed(10.8034); // Returns "10.80"
round2Fixed(10.8);    // Returns "10.80"

Various examples and tests (thanks to @t-j-crowder!):

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}
function naive(value, exp) {
  if (!exp) {
    return Math.round(value);
  }
  var pow = Math.pow(10, exp);
  return Math.round(value * pow) / pow;
}
function test(val, places) {
  subtest(val, places);
  val = typeof val === "string" ? "-" + val : -val;
  subtest(val, places);
}
function subtest(val, places) {
  var placesOrZero = places || 0;
  var naiveResult = naive(val, places);
  var roundResult = round(val, places);
  if (placesOrZero >= 0) {
    naiveResult = naiveResult.toFixed(placesOrZero);
    roundResult = roundResult.toFixed(placesOrZero);
  } else {
    naiveResult = naiveResult.toString();
    roundResult = roundResult.toString();
  }
  $("<tr>")
    .append($("<td>").text(JSON.stringify(val)))
    .append($("<td>").text(placesOrZero))
    .append($("<td>").text(naiveResult))
    .append($("<td>").text(roundResult))
    .appendTo("#results");
}
test(0.565, 2);
test(0.575, 2);
test(0.585, 2);
test(1.275, 2);
test(1.27499, 2);
test(1234.5678, -2);
test(1.2345678e+2, 2);
test("123.45");
test(10.8034, 2);
test(10.8, 2);
test(1.005, 2);
test(1.0005, 2);
table {
  border-collapse: collapse;
}
table, td, th {
  border: 1px solid #ddd;
}
td, th {
  padding: 4px;
}
th {
  font-weight: normal;
  font-family: sans-serif;
}
td {
  font-family: monospace;
}
<table>
  <thead>
    <tr>
      <th>Input</th>
      <th>Places</th>
      <th>Naive</th>
      <th>Thorough</th>
    </tr>
  </thead>
  <tbody id="results">
  </tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Solution 3:

I usually add this to my personal library, and after some suggestions and using the @TIMINeutron solution too, and making it adaptable for decimal length then, this one fits best:

function precise_round(num, decimals) {
   var t = Math.pow(10, decimals);   
   return (Math.round((num * t) + (decimals>0?1:0)*(Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals);
}

will work for the exceptions reported.

Solution 4:

I don’t know why can’t I add a comment to a previous answer (maybe I’m hopelessly blind, dunno), but I came up with a solution using @Miguel’s answer:

function precise_round(num,decimals) {
   return Math.round(num*Math.pow(10, decimals)) / Math.pow(10, decimals);
}

And its two comments (from @bighostkim and @Imre):

  • Problem with precise_round(1.275,2) not returning 1.28
  • Problem with precise_round(6,2) not returning 6.00 (as he wanted).

My final solution is as follows:

function precise_round(num,decimals) {
    var sign = num >= 0 ? 1 : -1;
    return (Math.round((num*Math.pow(10,decimals)) + (sign*0.001)) / Math.pow(10,decimals)).toFixed(decimals);
}

As you can see I had to add a little bit of “correction” (it’s not what it is, but since Math.round is lossy – you can check it on jsfiddle.net – this is the only way I knew how to “fix” it). It adds 0.001 to the already padded number, so it is adding a 1 three 0s to the right of the decimal value. So it should be safe to use.

After that I added .toFixed(decimal) to always output the number in the correct format (with the right amount of decimals).

So that’s pretty much it. Use it well 😉

EDIT: added functionality to the “correction” of negative numbers.

Solution 5:

One way to be 100% sure that you get a number with 2 decimals:

(Math.round(num*100)/100).toFixed(2)

If this causes rounding errors, you can use the following as James has explained in his comment:

(Math.round((num * 1000)/10)/100).toFixed(2)

Solution 6:

toFixed(n) provides n length after the decimal point; toPrecision(x)
provides x total length.

Use this method below

// Example: toPrecision(4) when the number has 7 digits (3 before, 4 after)
    // It will round to the tenths place
    num = 500.2349;
    result = num.toPrecision(4); // result will equal 500.2

AND if you want the number to be fixed use

result = num.toFixed(2);