Why Floating-Point Numbers are Imprecise
> 0.1 + 0.2 0.30000000000000004 > 0.1 + 1 - 1 0.10000000000000009
This copied from Speaking JavaScript, but the discussion is programming language irrelevant.
—
In the decimal system, all fractions are a mantissa m divided by a power of 10:
m/10e
.
So, in the denominator, there are only tens. That's why 1/3 cannot be expressed precisely as a decimal floating-point number—there is no way to get a 3 into the denominator. Binary floating-point numbers only have twos in the denominator. Let’s examine which decimal floating-point numbers can be represented well as binary and which can't. If there are only twos in the denominator, the decimal number can be represented:
0.5dec = 5/10 = 1/2 = 0.1bin 0.75dec = 75/100 = 3/4 = 0.11bin 0.125dec = 125/1000 = 1/8 = 0.001bin
Other fractions cannot be represented precisely, because they have numbers other than 2 in the denominator (after prime factorization):
0.1dec = 1/10 = 1/(2x5) 0.2dec = 2/10 = 1/5
You can't normally see that JavaScript doesn't store exactly 0.1 internally. But you can make it visible by multiplying it with a high enough power of 10:
> 0.1 * Math.pow(10, 24) 1.0000000000000001e+23
And if you add two imprecisely represented numbers, the result is sometimes imprecise enough that the imprecision becomes visible:
> 0.1 + 0.2 0.30000000000000004 > 0.1 + 1 - 1 0.10000000000000009
Due to rounding errors, as a best practice you should not compare nonintegers
directly. Instead, take an upper bound for rounding errors into consideration.
Such an upper bound is called a machine epsilon. The standard epsilon value for
double precision is 2e−53
:
var EPSILON = Math.pow(2, -53); function epsEqu(x, y) { return Math.abs(x - y) < EPSILON; }
epsEqu()
ensures correct results where a normal comparison would be
inadequate:
> 0.1 + 0.2 === 0.3 false > epsEqu(0.1+0.2, 0.3) true
blog comments powered by Disqus