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.
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.
Share Improve this question edited Jun 4, 2019 at 12:12 Boann 50k16 gold badges124 silver badges152 bronze badges asked Jun 4, 2019 at 4:46 WakeupWakeup 1651 silver badge8 bronze badges 2- ((1.5 <1.5) + Number.EPSILON) === ((2.5 <2.5) + Number.EPSILON) true (1.5 <1.5 + Number.EPSILON) === (2.5 <2.5 + Number.EPSILON) false – Pandiyan Cool Commented Jun 4, 2019 at 4:53
- It is related, in some way, to precision on floating point arithmetics. I believe next read can help to understand this concept: tutorialdocs./article/strange-javascript-number.html. There are a lot of articles about this. – Shidersz Commented Jun 4, 2019 at 5:26
4 Answers
Reset to default 7While 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 bee 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 putations 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 remended. But seriously: Not every puter 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
Number.EPSILON is:
difference between 1 and the smallest floating point number greater than 1
Let's say this number is 0.00000000000000000000000001 for the argument's sake.
Now,
1.5 < 1.5 + 0.00000000000000000000000001 === true
and, when the base number which you are adding this extremely small fraction grows larger, the precision calculations of the JS math evaluation finds its boundary.
2 < 2 + 0.00000000000000000000000001 === false
/*
┌─────────────────────────────────────┐
│ Number.EPSILON = 2¯⁵² ≈ 2.2 * 10¯¹⁶ │
└─────────────────────────────────────┘
Question 1: 2.5 < 2.5 + ε ?
2¯⁵¹
2¹↴ ⇩
10100000000000000000000000000000000000000000000000000 = 2.5 (JS)
+ 1 = ε
─────────────────────────────────────────────────────────────────────
┌┐ (01, 1 truncated )
101000000000000000000000000000000000000000000000000001
= 10100000000000000000000000000000000000000000000000000 = 2.5 (JS)
╰──────────────────── 52-bit ──────────────────────╯
⭐️ Conclusion ❌: 2.5 === 2.5 + ε
Question 2: 1.5 < 1.5 + ε ?
2¯⁵²
2⁰↴ ⇩
11000000000000000000000000000000000000000000000000000 = 1.5 (JS)
+ 1 = ε
─────────────────────────────────────────────────────────────────────
= 11000000000000000000000000000000000000000000000000001 = 1.5 + ε (JS)
╰──────────────────── 52-bit ──────────────────────╯
⭐️ Conclusion ✅: 1.5 < 1.5 + ε
*/
// --------------- log ---------------
const ε = Number.EPSILON;
[
2.5 < 2.5 + ε, // false❗️
1.5 < 1.5 + ε, // true
].forEach(x => console.log(x))