IEEE 双精度格式 2^53 或 2^54 中可表示的最大安全整数是多少?
Is the maximum safe integer representable in IEEE double format 2^53 or 2^54?
我在多个 post 中看到可以用 IEEE 双精度格式表示的最大安全整数是 2^53
:
但是有一个 post 说它是 2^54
:
Range within which all integers can be represented (including boundaries)
2^54 as an upper bound
-2^54 as a lower bound
这个答案是错误的还是我遗漏了什么?
编辑:
以上post现在似乎已更正。
But there is this one post which says it's 2^54:
Range within which all integers can be represented (including boundaries) 2^54 as an upper bound -2^54 as a lower bound
这个答案是错误的还是我遗漏了什么?
你没有遗漏什么。 IEEE 754 双精度二进制浮点数(“double”)不能表示的第一个整数是 2^53 + 1,完全在 -2^54..2^54 范围内。这可能是一个错字,因为可以表示 -2^53..2^53(含)范围内的所有整数。从 2^53 开始,只能表示 2 的倍数(因此 2^53 + 2 可以,但 2^53 + 1 不行)。由于 JavaScript 使用这种形式的双精度值,因此很容易看出它的作用:
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
对于双精度数,2^53 - 1 是最后一个所谓的“安全”整数,其中“安全”定义为“您可以将其加 1 并得到下一个整数”。 (事实上,JavaScript 甚至有一个值 2^53 - 1 的常量:Number.MAX_SAFE_INTEGER
。)当你加 1 时得到的下一个整数(当然)是 2^53——第一个整数格式无法再处理每个不同的整数。从 2^53 开始,double 只能以二为单位计数:2^53, 2^53 + 2, 2^53 + 4, ... 也就是说,此时它只能存储 2 的倍数。(然后后来,它只能按四 [4 的倍数] 计数,然后只能按八 [8 的倍数] 等)
为了完整性:格式可以(准确地)表示 很多 更大的整数,只是在那个数量级,它不能表示 all,因为格式使用基值(尾数)和指数,当你达到 2^53 时,指数只能表示偶数(2 的倍数)。稍后指数再次翻转,只能表示 4 的倍数,等等
让我们想象一下。 IEEE-754 相当复杂,但让我们举一个简化的例子( 不是 IEEE-754)只是为了解释的目的。假设我们有四位存储指数和八位存储尾数,假设我们只存储整数。这意味着指数 = 1,我们可以存储 0 到 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
我们的尾数已经用完了,所以我们通过使用指数 = 2 并使尾数成为 2 的倍数来牺牲范围的精度。现在我们只能按两位计数:
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
请注意,我们可以精确地表示值 256,即使它超出了尾数可以自行处理的范围 (0-255),因为我们使用指数将尾数值 (128) 加倍得到 256。这正是 2^53 在双精度数中发生的情况。但是我们不能表示 257,因为在那个量级我们只能处理 2 的倍数。这就是 2^53 + 1 在双精度数中发生的情况,我们不能表示它。不过我们可以表示 2^53 + 2。
以 IEEE-754 双精度格式表示的最大安全整数为 2^53 - 1。如果您尝试存储大于此值的值,它将不再安全。尽管可以存储更大的整数,但如果您想确保它保持安全,我建议不要这样做,因为它们在 2^53 - 1 以上会变得不精确。编辑:正如 Eric 的评论所解释的那样,值本身仍然是精确的,但是做算术的时候就是四舍五入的时候。
我在多个 post 中看到可以用 IEEE 双精度格式表示的最大安全整数是 2^53
:
但是有一个 post 说它是 2^54
:
Range within which all integers can be represented (including boundaries) 2^54 as an upper bound -2^54 as a lower bound
这个答案是错误的还是我遗漏了什么?
编辑:
以上post现在似乎已更正。
But there is this one post which says it's 2^54:
Range within which all integers can be represented (including boundaries) 2^54 as an upper bound -2^54 as a lower bound
这个答案是错误的还是我遗漏了什么?
你没有遗漏什么。 IEEE 754 双精度二进制浮点数(“double”)不能表示的第一个整数是 2^53 + 1,完全在 -2^54..2^54 范围内。这可能是一个错字,因为可以表示 -2^53..2^53(含)范围内的所有整数。从 2^53 开始,只能表示 2 的倍数(因此 2^53 + 2 可以,但 2^53 + 1 不行)。由于 JavaScript 使用这种形式的双精度值,因此很容易看出它的作用:
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
对于双精度数,2^53 - 1 是最后一个所谓的“安全”整数,其中“安全”定义为“您可以将其加 1 并得到下一个整数”。 (事实上,JavaScript 甚至有一个值 2^53 - 1 的常量:Number.MAX_SAFE_INTEGER
。)当你加 1 时得到的下一个整数(当然)是 2^53——第一个整数格式无法再处理每个不同的整数。从 2^53 开始,double 只能以二为单位计数:2^53, 2^53 + 2, 2^53 + 4, ... 也就是说,此时它只能存储 2 的倍数。(然后后来,它只能按四 [4 的倍数] 计数,然后只能按八 [8 的倍数] 等)
为了完整性:格式可以(准确地)表示 很多 更大的整数,只是在那个数量级,它不能表示 all,因为格式使用基值(尾数)和指数,当你达到 2^53 时,指数只能表示偶数(2 的倍数)。稍后指数再次翻转,只能表示 4 的倍数,等等
让我们想象一下。 IEEE-754 相当复杂,但让我们举一个简化的例子( 不是 IEEE-754)只是为了解释的目的。假设我们有四位存储指数和八位存储尾数,假设我们只存储整数。这意味着指数 = 1,我们可以存储 0 到 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
我们的尾数已经用完了,所以我们通过使用指数 = 2 并使尾数成为 2 的倍数来牺牲范围的精度。现在我们只能按两位计数:
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
请注意,我们可以精确地表示值 256,即使它超出了尾数可以自行处理的范围 (0-255),因为我们使用指数将尾数值 (128) 加倍得到 256。这正是 2^53 在双精度数中发生的情况。但是我们不能表示 257,因为在那个量级我们只能处理 2 的倍数。这就是 2^53 + 1 在双精度数中发生的情况,我们不能表示它。不过我们可以表示 2^53 + 2。
以 IEEE-754 双精度格式表示的最大安全整数为 2^53 - 1。如果您尝试存储大于此值的值,它将不再安全。尽管可以存储更大的整数,但如果您想确保它保持安全,我建议不要这样做,因为它们在 2^53 - 1 以上会变得不精确。编辑:正如 Eric 的评论所解释的那样,值本身仍然是精确的,但是做算术的时候就是四舍五入的时候。