Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is "(2.5 < 2.5 + Number.EPSILON)" false in JavaScript?

I want to find values less than a certain value in the array. I tried to use Number.EPSILON because the input value is not a definite value (eg 1.5000000000001).

I found something strange during the test:

>> (1.5 < 1.5 + Number.EPSILON) 
<- true 
>> (2.5 < 2.5 + Number.EPSILON)
<- false

Why is this? The test environment is the Chrome browser console.

like image 358
Wakeup Avatar asked Jun 04 '19 04:06

Wakeup


2 Answers

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 be false and
  • 2.5 < 2.5 + 4.4409E-16 would be true
like image 134
Marco13 Avatar answered Nov 08 '22 03:11

Marco13


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.

like image 6
CertainPerformance Avatar answered Nov 08 '22 01:11

CertainPerformance