0

I have seen in multiple posts that max safe integer representable in IEEE double format is 2^53: https://stackoverflow.com/a/3793950

https://stackoverflow.com/a/1848768

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER

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?

Edit:

Above post seems to be corrected now.

Dan
  • 2,694
  • 1
  • 6
  • 19

2 Answers2

2

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.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Sorry I thought I was clear, apparently not, I added the `safe` word to my title. So then what is 2^54 about in that post? is it wrong? – Dan Dec 20 '21 at 16:57
  • @Dan - Ah, didn't see the addition of "safe." 2^54 in that post is correct, it's just that at that magnitude, the format can't handle ones or twos anymore, from 2^54 onward it can only handle multiples of four because the exponent part of the double is 4. – T.J. Crowder Dec 20 '21 at 17:00
  • So then would the max size be 2^53 or 2^54? – Dan Dec 20 '21 at 17:01
  • @Dan - Neither. Again, much, much larger integers can be represented with the format, it's just that you can't represent the ones *around* them anymore, because at that point the format can't represent distinct integers. That happens *at* 2^53. That's the first value where it starts counting in twos. Which is why 2^53 - 1 is commonly called the last "safe" integer, it's the last one for which the exponent is 1. – T.J. Crowder Dec 20 '21 at 17:13
  • Thanks for all the info but you keep bringing up the fact that it can represent much larger values, *i know that*, i'm *only* talking about safe integers. All the above links are talking only about safe integers. But only one of them says 2^54 is the max SAFE integer size and the rest says it 2^53. So which is correct and why do they differ? – Dan Dec 20 '21 at 17:34
  • @Dan - Both 2^53 - 1 and 2^53 are held precisely in the format, no rounding involved. The sources I've seen have used the definition of "safe" that I use above: You can add 1 to it and get the next consecutive integer. By **that** definition, the only correct answer is 2^53 - 1, because you can do that with that value (2^53 - 1 + 1 is directly representable), and you can't do that with 2^53 (because 2^53 + 1 cannot be represented in the format). I suspect the confusion/discrepancy is because 2^53 is the first integer for which the format can no longer handle increments of 1. That doesn't ... – T.J. Crowder Dec 20 '21 at 17:37
  • ...change the fact that 2^53 is held precisely (so is 2^54, and 2^55). My understanding is that the max "safe" integer value is 2^53 - 1. – T.J. Crowder Dec 20 '21 at 17:38
  • So then why does that post say 2^54 is the max size, is it wrong or not, yes/no? I feel we are going in loops here – Dan Dec 20 '21 at 17:39
  • @Dan - As I said in the first paragraph above, the maximum safe integer (**not** range, safe) is 2^53 - 1. If that post claims 2^54 is *safe* using the definition of *safe* I've quoted above, it's wrong. – T.J. Crowder Dec 20 '21 at 17:41
  • Just want to confirm 32 bit floats as well, is `2^24` the max safe range? – Dan Dec 20 '21 at 17:43
  • @Dan - I don't know the details of floats, I'm afraid. The format is complex and I wouldn't want to hazard an uninformed guess. :-) (BTW, I've updated the answer above to specifically address your question about that post.) – T.J. Crowder Dec 20 '21 at 17:45
0

The maximum safe integer representable in IEEE-754 double precision format is 2^53 - 1. If you try storing a value larger than this it is no longer safe. Although it is possible to store larger integers, I'd advise against it if you want to ensure that it stays safe, since they would become imprecise above 2^53 - 1. Edit: As explained by the comment of Eric, the values themselves are still precise, but when doing arithmetic is when they round.

  • So that answer is wrong? – Dan Dec 20 '21 at 17:20
  • In what way is it unsafe to store `2^53` as a double? – Sneftel Dec 20 '21 at 18:45
  • It is unsafe in that integer values (**above** `2^53 - 1`) are no longer precise. `2^53` and `2^53 + 1` are the same thing, since from there onwards only **integers** which are multiples of 2 can be represented. –  Dec 20 '21 at 19:01
  • 1
    @Ralf: Per the IEEE-754 standard, all floating-point values are precise: Each floating-point datum that is not an infinity or a NaN represents one number exactly. The floating-point number 2^53 represents 2^53. It does not represent 2^53+1, and 2^53+1 is not represented with 2^53 or with any other value in the IEEE-754 binary64 format. We simply say that 2^53+1 is not represented or that, when converted to binary64 with rounding-to-nearest-ties-to-even, the results is 2^53, not that 2^53+1 is not precise… – Eric Postpischil Dec 21 '21 at 00:46
  • … This is important: Floating-point **values** do not approximate numbers. Floating-point **operations** approximate real-number arithmetic. When a floating-point operation is performed, the result produce is the real-number arithmetic result rounded to the nearest representable value. This distinction is essential for analyzing floating-point software, for designing it, for debugging it, and for writing proofs about it. – Eric Postpischil Dec 21 '21 at 00:47
  • Oh, I thought it had to do with representation aswell. Thanks for clearing that up, now I know better for next time. (And hopefully don't make a mistake like that again) –  Dec 21 '21 at 07:53