impulsgraw
impulsgraw

Reputation: 897

Why does Number.EPSILON act different on different operands?

Consider condition

(-1 - Number.EPSILON < -1) === (-2 - Number.EPSILON < -2)

This example is executed in Chrome console on my machine to false (?!), but I can't get why (left part is true, when right part is false). The whole condition also executes to false in Edge and Firefox, but to true in Internet Explorer (??!?).

What happens on Chrome console

Upvotes: 2

Views: 811

Answers (2)

melpomene
melpomene

Reputation: 85827

Floating-point numbers are centered around zero. That is, they're densest around 0; the bigger your numbers get in magnitude, the fewer floating-point numbers exist in that region of the number line.

Bad ASCII approximation:

---------------------------------------(-1)-----(0)-----(1)------------------------------------------
 .        .     .    .    .  .  .  . . . .................. . . .  .  .  .    .    .    .        .

The upper line represents the real numbers; the dots below mark possible floating-point values. There are many possible floating-point values between -1 and 1, allowing for very fine-grained distinction. The further away you get from 0, the sparser the possible floating-point values become, which means bigger numbers are stored with less precision.

Number.EPSILON is the distance between 1 and the next higher floating-point number. That means 1 + Number.EPSILON exists as a floating-point value and can be represented exactly. The same applies to -1 - Number.EPSILON, which is the same value, but with a negative sign.

However, 2 + Number.EPSILON does not exist. Because 1 is closer to 0 than 2 is, floating-point numbers are more dense around 1 than around 2. In particular, the difference between 2 and the next higher floating-point value is bigger than Number.EPSILON (in fact, I wouldn't be surprised if it turned out to be 2 * Number.EPSILON). Because 2 + Number.EPSILON cannot be represented exactly, it is rounded to the nearest floating-point number, which turns out to be 2 itself:

console.log(2 + Number.EPSILON === 2);  // true

As for Internet Explorer: It does not support Number.EPSILON, so

-1 - Number.EPSILON < -1

is evaluated as

-1 - undefined < -1

which is Number.NaN < -1, which (like all comparisons involving NaN) evaluates to false.

Upvotes: 4

Oleg Imanilov
Oleg Imanilov

Reputation: 2751

JavaScript number is double precision floating number (64 bit). Where bits 0-51 represents fraction and rest bits are for exponent.

If you try to convert Number.EPSILON to binary - You'll see that it is:

>  Number.EPSILON.toString(2)
'0.0000000000000000000000000000000000000000000000000001'

The first bit is 0, so if you add 1 - it becomes 1, and it still fits into fraction bits:

>  (1 + Number.EPSILON).toString(2)
'1.0000000000000000000000000000000000000000000000000001'
//---- So -----
(1+Number.EPSILON) !== 1

But when you add 2 (binary 10) it catches additional bit on the left - so the right (less significant) bit is cut - to fit into fraction bits.

2 + Number.EPSILON
10.000000000000000000000000000000000000000000000000000|1  <- right bit dropped
                                                      ^
                                                      51 bit
//---- So -----
(2+Number.EPSILON) === 2

Upvotes: 2

Related Questions