Hacker News new | ask | show | jobs
by 29athrowaway 1895 days ago
You cannot compare floating point numbers like that.

The equality test in floating point numbers is comparing against the epsilon.

    Math.abs(0.3 - (0.1 + 0.2)) < Number.EPSILON
Which is the same you other languages.

Using the epsilon for comparison is not mentioned in the article. Floating point absorption is also not mentioned in the article.

This entire discussion and the fact this is on the front page of HN is pretty disappointing and sad.

Is this really a surprise for you? if it is... have you ever implemented any logic involving currency? You may want to take another look at it.

3 comments

Well, that was trivially easy to disprove with a little jiggling of numbers in the console:

  Math.abs(1.8 - (0.1 + 0.2 + 0.9 + 0.6)) < Number.EPSILON
returns false.

Also, you generally really shouldn't be implementing any currency logic using floating point numbers, yikes. Stick to integers that represent the value in cents, or tenths of cents, or similar. Or, even better, a DECIMAL data type if your platform supports it.

I genuinely hope you've never written financial software that judges if the results of two calculations are equal via the method you've described.

I never said you should use floating-point numbers for currency. I said that if you are not aware of the shortcomings of floating-point numbers (the only numeric type in JavaScript, not counting workarounds like typed arrays) you should not be working with values representing currencies until you do.

Fixed-point numbers (aka "decimal" type in Java, C# and others) are the preferred way to deal with this problem as I mentioned in this same discussion hours ago.

Now, in the example you presented, you have a round-off error amplication problem where the round-off error grows larger than the epsilon. You can avoid that using the Kahan summation algorithm.

https://en.wikipedia.org/wiki/Kahan_summation_algorithm

    const arr = [0.1, 0.2, 0.9, 0.6];
    let sum = 0.0;
    let c = 0.0;
    for(let i = 0, l = arr.length; i < l; i++) {
        y = arr[i] - c;
        t = sum + y;
        c = (t - sum) - y;
        sum = t;
    }
Now you evaluate sum, and it's 1.8 as expected.
^ And of course a comment mentioning the correct way of doing things got downvoted.
rounding error after multiple operations can be more than just epsilon
That's why there are algorithms to do those multiple operations.
> have you ever implemented any logic involving currency? You may want to take another look at it.

Floating point arithmetic is good enough for science, should be good enough for commerce too, no? Why is commerce special?