Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are two different numbers equal in JavaScript?

I've been messing around with a JavaScript console, when I suddenly decided to try this:

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Surprisingly, they're equal: Strange output

Why does it happen? They're clearly different numbers (even the 0xFFFF...FFFF is one digit shorter)

If I add a F to the 0xFFFF...FF, they're not equal anymore: 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Even stranger output

Is this expected behaviour?

like image 867
rev Avatar asked Aug 07 '14 19:08

rev


People also ask

Why does JavaScript use === instead of ==?

Use === if you want to compare couple of things in JavaScript, it's called strict equality, it means this will return true if only both type and value are the same, so there wouldn't be any unwanted type correction for you, if you using == , you basically don't care about the type and in many cases you could face ...

What is meant by === in JavaScript?

The strict equality operator ( === ) checks whether its two operands are equal, returning a Boolean result. Unlike the equality operator, the strict equality operator always considers operands of different types to be different.

What is double equal in JavaScript?

With double equals, JavaScript will convert the types of the two values to be exactly the same before doing an equality check. This means that if you have the following code 1 == '1' then JavaScript will convert the string '1' into a number so that the equality check is comparing two numbers.


2 Answers

All numbers in JavaScript are internally represented by 64-bit floating point numbers (see §4.3.19 of the specification). That means it can exactly represent every integer from 0 up to 9007199254740992 (hex value 0x20000000000000). Any integers greater than that (or less than it's negative counterpart) may need to be rounded to the closest approximate value.

Observe:

9007199254740992 === 9007199254740993 > true 

However, two numbers that are rounded to sufficiently different approximate values still evaluate to different values when you compare them. For example:

9007199254740992 === 9007199254740994 > false 

This is what you're seeing in the second snippet where you add another F digit.


Note: The ECMAScript specification now define Number.MAX_SAFE_INTEGER as a global constant equal to 9007199254740991.

like image 153
p.s.w.g Avatar answered Oct 14 '22 01:10

p.s.w.g


0x100000000000000 == 0xFFFFFFFFFFFFFF gives true while 0x10000000000000 == 0xFFFFFFFFFFFFF gives false. So the former is the "limit", say.

Let's analyze the numbers: 52 bits for 0xFFFFFFFFFFFFF and one additional bit for 0x10000000000000 in the internal representation.

EDIT: Numbers of such magnitude are not represented by long ints but by double precission floats. This is because they exceed the 32bit representation of an integer value every number in javascript is represented as IEEE754 double precision floating point.

When you represent any IEEE754 Double Precission FP Number internally, you get:

0111 1111 1111 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 

Where (0) is the sign bit, (1) the exponent bits, and (2) the mantissa bits.

If you compare in JavaScript 0.5 == 5 * 0.1 you will get true even when that operation has floating imprecission (i.e. you get some error). So Javascript tolerates a small error in floating point operations so operations like that give true, as the common sense tells.

Edit - There's something wrong I wrote about the Mantissa: Yes, every Mantissa starts with 1 (it is said that such mantissa is normalized), BUT that 1 is not stored in a normalized number (each nonzero exponent has only normalized numbers. mantissas for numbers with exponent 000 0000 0000 do not follow this rule). This means that every normalized mantissa has 52 explicit bits, and an implicit 1.

Now: what about the 52 bits? Notice that the 0xFF... has 52 bits length. This means that it will be stored as: 0 for the sign (it is positive), 52 for the exponent, and 52 "1" digits in the mantissa (see a final note at the foot of this answer). Since one "1" is implicit, we'll store 51 "1" and one "0".

0100 0011 0010 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110  (exponent 1075 corresponds to actual exponent 52) 

AND the other number has 53 bits: one "1" and 52 "0". Since the first "1" is implicit, it will be stored like:

0100 0011 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000  (exponent 1076 corresponds to actual exponent 53) 

Now it's time to compare values. They will compare in equality of conditions: first we take sign and exponent to the comparisson. If they're equal, we consider the mantissa.

Comparisson here takes place considering a small error being tolerated as a product of rounding. Such epsilon is taken into account (epsilon is about =2^-53) and FP ALU detects that, relatively, those numbers differ only in such epsilon, so they seem to be equal only in that context (there are many times where this does not save you as in cases of 0.3 == 0.2 + 0.1, being each of three numbers binary-non-representable, in contrast to 0.5 which is, and can tolerate an error against 0.1 + 0.4).

Note About the mantissa and the FP representation: The Mantissa is always, conceptually, lower than 1. If you want to represent a higher number, you must conceive it using an exponent. Examples:

  • 0.5 is represented as 0.5 * 2 ^ 0 (consider the right operator precedence in math).
  • 1 is represented not as 1 * 2 ^ 0 since the mantissa is always lower than 1, so the representation will be 0.5 * 2 ^ 1.
  • 65, which has binary representation as 1000001, will be stored as (65/128) * 2 ^ 7.

These numbers are represented as (remember: the first "1" is implicit since these exponents are for normalized numbers):

0011 1111 1111 0000 ... more 0 digits  (exponent 1023 stands for actual exponent 0, mantissa in binary repr. is 0.1, and the first "1" is implicit).  0100 0000 0000 0000 ... more 0 digits  (exponent 1024 stands for actual exponent 1, mantissa in binary repr. is 0.1, and the first "1" is implicit). 

and

0100 0000 0110 0000 0100 0000 0000 0000  (exponent 1030 stands for actual exponent 7, mantissa in binary repr. is 0.1000001, and since the first "1" is implicit, it is stored as 0000 0100 0000...) 

Note About the exponent: Lower precission can be achieved by allowing negative exponents as well: Exponents seem positive -unsigned- but the reality is that you must subtract 1023 (called "bias") to that number to get the actual exponent (this means that exponent "1" actually corresponds to 2^(-1022)). Translating this to a 10-based power, the lowest exponent is -308 for decimal numbers (considering also the mantissa possition as I will show later). The lowest positive number is:

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 

which is: (1 * 2^-52) * 2^-1023 being the first -52 given by the mantissa and the -1023 by the exponent. The final one is: 1 * 2^(-1075), which goes towards the 10^-308 always told.

The lowest exponent is 000 0000 0000 corresponding to (-1023). There's a rule: Every mantissa must begin with (an implicit) "1" or have this exponent. On the other side, the highest exponent could be 111 1111 1111, but this exponent is reserved for special pseudonumbers:

0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 

corresponds to +Infinity, while:

1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 

corresponds to -Infinity, and any pattern with a nonzero mantissa, like:

?111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 

correspond to NaN (not a number; ideal to represent stuff like log(-1) or 0/0). Actually I'm not sure what mantissas are used for NaN (either quiet or signaling NaN). The question mark stands for any bit.

like image 43
Luis Masuelli Avatar answered Oct 14 '22 00:10

Luis Masuelli