4

I'm dealing with relatively small and simple numbers. I first tried to do the rounding (2 signs after digit) with infamous toFixed. It's a known issue: sometimes it works incorrectly. But what struck me is the fact that it also works inconsistently:

(0.395).toFixed(2); // "0.40"
(0.295).toFixed(2); // "0.29"

These numbers are very similar, 3 signs after digit and yet different behavior.

So, I decided to switch to using Math.round. Shortly, I encountered another problem:

Math.round(0.35055 * 10000) / 100; // produces 35.05 instead of 35.06

Is Math.round also problematic? Then, what method should be used?

Zaziro
  • 405
  • 4
  • 8
  • FWIW the issue with the last one occurs before it gets to `Math.round`: `0.35055 * 10000` -> `3505.4999999999995` – James Thorpe Oct 16 '17 at 13:06
  • Why is `0.35055 * 1000` equal to `3505.4999999999995` instead of `3505.5`? – Manav Oct 16 '17 at 13:08
  • Check these answers: [here](https://stackoverflow.com/q/5490687/1823841), [here](https://stackoverflow.com/q/10768083/1823841) – palaѕн Oct 16 '17 at 13:09
  • @Manav See [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) for the gory details. This Q is _almost_ a dupe of it, but I suspect the `toFixed` rounding behaviour is probably explained somewhere too. – James Thorpe Oct 16 '17 at 13:09

2 Answers2

4

Unfortunately JavaScript is known to have such precision issues, issues that are better explained in the following question: Is floating point math broken?, as pointed in the comments.

If you require a greater degree of numerical accuracy, I would suggest you to use a library such as BigNumber, which also comes with its own toFixed method.

Your example would look something like this:

var a = new BigNumber('0.35055');
a = a.times(10000)
a = a.dividedBy(100)
console.log(a.toFixed(2)); //would log "35.06"

For brevity you can also chain the operations, like this: a.times(10000).dividedBy(100).toFixed(2)

BBog
  • 3,630
  • 5
  • 33
  • 64
  • 1
    i fixed my problem by means of the following workaround: `Math.round((0.35055 * 100) * 100) / 100;` But obviously, your solution is better, thanks. – Zaziro Oct 17 '17 at 13:41
0

I think this is working as designed. Keep in mind these numbers are stored in base 2, so there is a loss of precision when converting to and from base 10. And you have to look at these conversions if you want to understand why it looks inconsistent. If you have a fixed number of decimals that you want to keep precisely, you can use integers for operations and convert only for display.

Damien
  • 3,060
  • 1
  • 25
  • 29