JavaScript Pitfalls & Tips: toFixed

Number.prototype.toFixed(n) is a number formatting method that shows n-th digits after the decimal point. It seems to rounds (n+1)-th digits. But, sometimes, it does not round up. For example, the value of 2.55.toFixed(1) is 2.5, not 2.6 which is correct. This example is even in MDN documents.

This strange phenomenon is due to the inaccuracy of floating points notation. What you get if you add 0.05 to 2.55? You must get a 2.6. But, in 64-bit floating points notation, the answer is as follows:

1
2
> 2.55 + 0.05
2.5999999999999996

Solution

If we want to round the digit correctly, we should not use toFixed(n). There are the following alternatives.

Do it in Integer

The first alternative is not using floating points, but using integer. For example, set all the currency unit to cents, not dollars.

Adding 0.5 and get the integer part in 64-bit floating points notation does no harm. Using this feature we can write another toFixed function as follows:

1
2
3
4
5
6
7
8
9
10
/**
* Return the fixed point notation.
* @param {number} n - the number to be fixed notation
* @param {number} d - the number of digits after decimal points
* @returns {string}
*/
function toFixed(n, d) {
const tenToD = Math.pow(10, d);
return (Math.floor(n * tenToD + 0.5) / tenToD).toFixed(d);
}

You may use Math.round instead of Math.floor and + 0.5.

1
2
3
4
function toFixed(n, d) {
const tenToD = Math.pow(10, d);
return (Math.round(n * tenToD) / tenToD).toFixed(d);
}

Arbitrary precision libraries

If you need to calculate more precisely than 64-bit floating points notation, you would better use one of the arbitrary precision libraries. Most arbitrary precision libraries can designate precision in decimal places. Moreover, most of the libraries support toFixed.

For example, you can see the toFixed of big.js works correctly as follows:

1
2
3
4
5
const Big = require('big.js');

const test = Big('2.55').toFixed(1);
console.log(test); // 2.6
console.log(test === '2.6'); // true

References