Accuracy problem of floating point numbers
Floating point numbers can’t represent all decimal fractions precisely. The accuracy problem stems from the fact that underneath the value is stored in a binary which doesn’t match perfectly the decimal.
To show the problem consider the following:
0.1 + 0.2 === 0.3 // => false
This looks strange. It seems so obvious to us, but we are used to calculating things in decimal. With floating point numbers, the number we see is an only approximation. With that knowledge we can recognize why the previous test was failing:
0.1 + 0.2 // => 0.30000000000000004
The accuracy problem becomes especially pronounced when money is involved. Fortunately, the solution is quite simple. Scale all the values to whole cents (euro cents, centavos, kuruş, etc). Then do all the calculations on scaled values. Once calculations are done and ready to be displayed (or to be stored) convert it back to a fractional representation.
What it means is that the floating point number behaves like a regular integer and there’s no accuracy problem in doing calculations on those.
To help myself when working on those calculations I like to create two simple functions:
const cents = n => n * 100; const dollars = n => n / 100;
With those functions, the problematic case from the beginning is fixed:
cents(0.1) + cents(0.2) === cents(0.3) // => true
Using descriptive names makes it easy to see when things are being converted and in which direction.
Another helpful function is
display which converts the number directly to a string:
const display = n => (n / 100).toFixed(2);
That way monetary value can be stored in cents directly.