0

I am trying to round a number in Javascript to two decimal places. I consulted the following Stack Overflow post for guidance:

Round to at most 2 decimal places (only if necessary)

Now, in that post, there seems to be two ways to accomplish this. One is toFixed(2), and the other is Math.round((num + Number.EPSILON) * 100) / 100, with the latter seeming to be the most accepted way of doing it.

So, given the number 249.025, this works. Both methods produce 249.03 as expected. However, given the number 294.025 (flip the 4 and the 9), both methods fail, and produce 294.02.

Here is a JSFiddle to demonstrate: https://jsfiddle.net/uj8x4khn/

My question: Is there a rounding method that will work on both of those numbers (and any number)?

Also curious: Why would it work on one number and not a very similar number?

smoothgrips
  • 407
  • 3
  • 15
  • So... [toFixed(2)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed)? There also isn't really such thing as "only if necessary": if you care enough to round fractions to 2 decimal places, integers should show as ending in `.00` in that same presentation context. – Mike 'Pomax' Kamermans Aug 27 '20 at 21:57
  • Re last question: because floating point, and `EPSILON` is very, very small. – Dave Newton Aug 27 '20 at 22:01
  • @Mike'Pomax'Kamermans, I need to round `294.025` to `294.03` – smoothgrips Aug 27 '20 at 22:01
  • Noting that `Number(Math.round(294.025 + 'e' + 2) + 'e-' + 2)` may or may not work better for your particular scenario. If you need actual precision you may want to use a real math library. – Dave Newton Aug 27 '20 at 22:05
  • The answer by MarkG, in the very question you linked, solves your problem. – Blackhole Aug 27 '20 at 22:06
  • @smoothgrips welcome to IEEE floats. 294.025 is not "294 and 25 one thousands". Just because the letters look that way, doesn't mean that's the _actual_ number unless you use a true precision datatype, which JS's `Number` very much is not. – Mike 'Pomax' Kamermans Aug 27 '20 at 23:29

1 Answers1

1

The reason is that 294.025 is not exactly 294.025, but slightly less, because floating point cannot represent that number precisely. You'll notice that when you multiply it with 100, it is even not displayed as 29402.5, but as 29402.499999999999

The best way to solve this, is to go to integer arithmetic only. If you are working with monetary units, calculate everything in cents, not in the currency's main unit. If you are working with 4 decimals, then start out with integers and denote the 4 least significant digits of an integer as referring to the decimal part. Only when you actually want to display it, divide it by the appropriate power of 10. But never store that result for further calculation.

trincot
  • 317,000
  • 35
  • 244
  • 286