But there is this one post which says it's 2^54: https://stackoverflow.com/a/12445693
Range within which all integers can be represented (including boundaries) 2^54 as an upper bound -2^54 as a lower bound
Is this answer wrong or am I missing something?
You're not missing something. The first integer that IEEE 754 double-precision binary floating point ("double") can't represent is 2^53 + 1, which is well within the claimed -2^54..2^54 bounds. It's probably a typo, since all integers in the range -2^53..2^53 (inclusive) can be represented. From 2^53 onward, only multiples of 2 can be represented (so 2^53 + 2 is fine, but not 2^53 + 1). Since JavaScript uses this form of double, it's easy to see this in action:
const x = 2**53;
const y = x + 1;
console.log(x.toLocaleString()); // 9,007,199,254,740,992
console.log(y.toLocaleString()); // 9,007,199,254,740,992 -- the same
console.log(x === y); // true
const z = x + 2;
console.log(z.toLocaleString()); // 9,007,199,254,740,994
With a double, 2^53 - 1 is the last so-called "safe" integer, where "safe" is defined as "you can add 1 to it and get the next integer." (In fact, JavaScript even has a constant for the value 2^53 - 1: Number.MAX_SAFE_INTEGER
.) The next integer you get when you add 1 is (of course) 2^53 — the first integer at which the format can no longer handle every distinct integer. Starting with 2^53, a double can only count by twos: 2^53, 2^53 + 2, 2^53 + 4, ... That is, at that point it can only store multiples of 2. (And then later, it can only count by fours [multiples of 4], and then later only by eights [multiples of 8], etc.)
For completeness: The format can (exactly) represent much larger integers, it's just that at that magnitude, it can't represent all of them, because the format uses a base value (mantissa) and an exponent, and when you reach 2^53, the exponent is such that only even numbers (multiples of 2) can be represented. Later the exponent rolls over again and only multiples of 4 can be represented, etc.
Let's visualize this a bit. IEEE-754 is fairly complex, but let's take a simplified example (which isn't IEEE-754) just for the purposes of explanation. Suppose we have four bits to store an exponent and eight bits to store a mantissa, and let's say we only store whole numbers. That means with exponent = 1, we can store the values 0 through 255:
CONCEPTUAL, *NOT* IEEE-754!
Exponent (binary) Mantissa (binary) Result Value (decimal)
0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 1 1
0 0 0 1 0 0 0 0 0 0 1 0 2
...
0 0 0 1 1 1 1 1 1 1 1 0 254
0 0 0 1 1 1 1 1 1 1 1 1 255
We're maxed out in the mantissa, so we sacrifice precision for range by using exponent = 2 and making the mantissa multiples of 2. Now we can only count by twos:
CONCEPTUAL, *NOT* IEEE-754!
Exponent (binary) Mantissa (binary) Result Value (decimal)
0 0 0 2 0 0 0 0 0 0 0 1 2
0 0 0 2 0 0 0 0 0 0 1 0 4
...
0 0 0 2 0 1 1 1 1 1 1 1 254
0 0 0 2 1 0 0 0 0 0 0 0 256
0 0 0 2 1 0 0 0 0 0 0 1 258
...
0 0 0 2 1 1 1 1 1 1 1 0 508
0 0 0 2 1 1 1 1 1 1 1 1 510
Notice that we can represent the value 256 precisely, even though it's out of the range the mantissa could handle on its own (0-255), because with the exponent we take the mantissa value (128) and double it to get 256. That's exactly what happens with 2^53 in a double. But we can't represent 257, because at that magnitude we can only handle multiples of 2. That's what happens with 2^53 + 1 in a double, we can't represent it. We can represent 2^53 + 2 though.