Why are two different numbers equal in JavaScript?
This is obviously overflow or rounding. Work out mathematically the magnitude of the numbers and check against the largest number.
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 be0.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.
The following hexa decimal number:
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
is stored as the IEEE 754 standard float value:
1.3407807929942597e+154
You add 1 to this number and it becomes:
0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
which is also stored as:
1.3407807929942597e+154
Both numbers are lie outside the range of numbers that can be accurately represented by the JavaScript Number (ref). In the above example, both numbers end up having the same internal representation, hence they are, sort of, equal.
Reminder: one should not compare floating point numbers using the equality operator (ref).
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
.