skip to Main Content

I know the toFixed() function isn’t meant to be used for rounding, but I still can’t seem to make sense of why it sometimes rounds up and sometimes rounds down when the number ends in .5

Take a look at these examples:

toFixed(0) example

(0.5).toFixed(0)
> '1' // round up

(1.5).toFixed(0)
> '2' // round up

(2.5).toFixed(0)
> '3' // round up

(3.5).toFixed(0)
> '4' // round up

(4.5).toFixed(0)
> '5' // round up

(5.5).toFixed(0)
> '6' // round up

(6.5).toFixed(0)
> '7' // round up

(7.5).toFixed(0)
> '8' // round up

(8.5).toFixed(0)
> '9' // round up

(9.5).toFixed(0)
> '10' // round up

Consistent behavior for this example.


toFixed(1) example

(0.05).toFixed(1)
> '0.1' // round up

(1.05).toFixed(1)
> '1.1' // round up

(2.05).toFixed(1)
> '2.0' // round down

(3.05).toFixed(1)
> '3.0' // round down

(4.05).toFixed(1)
> '4.0' // round down

(5.05).toFixed(1)
> '5.0' // round down

(6.05).toFixed(1)
> '6.0' // round down

(7.05).toFixed(1)
> '7.0' // round down

(8.05).toFixed(1)
> '8.1' // round up

(9.05).toFixed(1)
> '9.1' // round up

This is where things start to break down. It seems like the first digit affects the rounding for some reason. Sometimes it rounds up and sometimes it rounds down.


toFixed(2) example

(0.005).toFixed(2)
> '0.01' // round up

(1.005).toFixed(2)
> '1.00' // round down

(2.005).toFixed(2)
> '2.00' // round down

(3.005).toFixed(2)
> '3.00' // round down

(4.005).toFixed(2)
> '4.00' // round down

(5.005).toFixed(2)
> '5.00' // round down

(6.005).toFixed(2)
> '6.00' // round down

(7.005).toFixed(2)
> '7.00' // round down

(8.005).toFixed(2)
> '8.01' // round up

(9.005).toFixed(2)
> '9.01' // round up

Even stranger is that it’s not always the same first digits that affect the rounding. You can see the results here are similar to the toFixed(1) example, but not exactly the same.


Is this a bug, some kind of browser issue, or expected behavior?

My system:

  • Apple M1 Pro
  • Chrome Version 116.0.5845.187

2

Answers


  1. This is actually expected behavior with floating point precision in JS, as well as many other programming languages.

    Here’s a stack overflow thread discussing. Dealing with float precision in Javascript

    Login or Signup to reply.
  2. You could use my following code to round numbers to some decimal point with up to 10 decimals (the bigger you could get some rounding errors from toFixed():

    const round = (num, digits = 2, precision = 10) => {
      const str = num.toFixed(precision), idx = str.indexOf('.');
      if (idx < 0) return num;
      const left = str[idx + 1 + digits]; // round this char
      if (!left || left === '0') return num;
      return parseFloat(str.slice(0, idx + 1 + digits)) + 
          (left >= 5) * (str[0] === '-' ? -1 : 1) / 10 ** digits;
    }
    
    const toFixed = (num, decimals = 2) => round(num, decimals).toFixed(decimals);
    
    [[0.0000000049, 9], [6.5, 0], [1000000.0000055, 6], [0.0000055, 6], [0.0049555, 4], [6.05, 1], 35.855, 35.8549999, [-35.8549999, 3], 35.895, 35.8, 35].forEach(num => 
      console.log(JSON.stringify(num), '->', toFixed(...[].concat(num))));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search