Why is "(2.5 < 2.5 + Number.EPSILON)" false in JavaScript?
While Number.EPSILON
itself can be represented precisely, this is no guarantee that adding values to it (or manipulating it any further at all) will result in perfect precision. In this case, 1.5 + Number.EPSILON
results in a number slightly higher than 1.5:
console.log(1.5 + Number.EPSILON);
Which is clearly greater than 1.5. On the other hand, adding 2.5 to Number.EPSILON
results in exactly 2.5 - the precision you were hoping for was lost during the addition process.
console.log(2.5 + Number.EPSILON);
2.5 < 2.5
evaluates to false
, as expected.
Floating point numbers have a limited precision. Depending on the language and architecture, they are usually represented using 32 bits (float
) or 64 bits (double
, as of "double precision"). Although things become blurry in an untyped language like JavaScript, there is still an actual machine underneath all this, and this machine has to perform floating point arithmetic.
The problem is that the results of certain computations cannot be represented accurately, given the limited precision. This is explained with some examples on the Wikipedia page about floating point artithmetic.
For people who want all the nitty-gritty details, the article about What Every Computer Scientist Should Know About Floating-Point Arithmetic is usually recommended. But seriously: Not every computer scientist needs to know all this, and I'm pretty sure that only a handful people in the world have actually read the whole thing....
As an overly suggestive example: Imagine you had 5 digits to store a number. When you then have an addition like
10000.
+ 0.00001
--------------------
= 10000.
the .00001
part will basically be "truncated" because it does not fit into the 5 digits.
(That's not exactly how this works, but should get the idea across)
The actual value for Number.EPSILON
, according to the documentation, is approximately 2.22 * 10-16, and is the "difference between 1 and the smallest floating point number greater than 1". (This is sometimes referred to as an ULP, Unit In The Last Place).
So adding this value to 1.0 will result in a different number. But adding it to 2.5 will not result in a different number, because the difference between 2.5 and the smallest floating point number greater than 2.5 is larger than this epsilon. The epsilon is thus truncated, like the .00001
in the example above.
Some languages/libraries may offer an "ulp" function that returns the difference between a given value and the next larger representable value. For example, in Java, you have
System.out.println(Math.ulp(1.0)); // Prints 2.220446049250313E-16
System.out.println(Math.ulp(2.5)); // Prints 4.440892098500626E-16
The first one obviously is what is stored in Number.EPSILON
. The second one is the value that should yield a different value when added to 2.5. So
2.5 < 2.5 + 4.4408E-16
would befalse
and2.5 < 2.5 + 4.4409E-16
would betrue