许多较早的答案显示 9007199254740992 === 9007199254740992 + 1 是 true 以验证 9,007,199,254,740,991 是最大且安全的整数。
但是如果我们继续做积累呢:
input: 9007199254740992 + 1 output: 9007199254740992 // expected: 9007199254740993
input: 9007199254740992 + 2 output: 9007199254740994 // expected: 9007199254740994
input: 9007199254740992 + 3 output: 9007199254740996 // expected: 9007199254740995
input: 9007199254740992 + 4 output: 9007199254740996 // expected: 9007199254740996
我们可以看到,在大于9,007,199,254,740,992的数字中,只有偶数可表示。
这是一个解释双精度 64 位二进制格式如何工作的条目。让我们看看如何使用这种二进制格式保存(表示)9,007,199,254,740,992。
用一个简短的版本从4,503,599,627,370,496进行演示:
1 . 0000 ---- 0000 * 2^52 => 1 0000 ---- 0000.
|-- 52 bits --| |exponent part| |-- 52 bits --|
在箭头的左侧,我们有位值 1,以及一个相邻的小数点。通过消耗左边的指数部分,小数点向右移动了 52 步。小数点在最后,我们得到纯二进制的 4503599627370496。
现在让我们继续将小数部分加 1,直到所有位都设置为 1,这等于十进制的 9,007,199,254,740,991。
1 . 0000 ---- 0000 * 2^52 => 1 0000 ---- 0000.
(+1)
1 . 0000 ---- 0001 * 2^52 => 1 0000 ---- 0001.
(+1)
1 . 0000 ---- 0010 * 2^52 => 1 0000 ---- 0010.
(+1)
.
.
.
1 . 1111 ---- 1111 * 2^52 => 1 1111 ---- 1111.
由于 64 位双精度格式严格为小数部分分配 52 位,如果再加 1,则没有更多位可用,所以我们可以将所有位设置回 0,并操作指数部分:
┏━━▶ This bit is implicit and persistent.
┃
1 . 1111 ---- 1111 * 2^52 => 1 1111 ---- 1111.
|-- 52 bits --| |-- 52 bits --|
(+1)
1 . 0000 ---- 0000 * 2^52 * 2 => 1 0000 ---- 0000. * 2
|-- 52 bits --| |-- 52 bits --|
(By consuming the 2^52, radix
point has no way to go, but
there is still one 2 left in
exponent part)
=> 1 . 0000 ---- 0000 * 2^53
|-- 52 bits --|
现在我们得到 9,007,199,254,740,992,对于大于它的数字,格式只能处理 2 的增量,因为小数部分的每个增量 1 都会结束在指数部分乘以左边的 2。这就是为什么双精度64位二进制格式不能容纳大于9,007,199,254,740,992的奇数:
(consume 2^52 to move radix point to the end)
1 . 0000 ---- 0001 * 2^53 => 1 0000 ---- 0001. * 2
|-- 52 bits --| |-- 52 bits --|
按照这种模式,当数字大于 9,007,199,254,740,992 * 2 = 18,014,398,509,481,984 时,只能保留 4 倍的分数:
input: 18014398509481984 + 1 output: 18014398509481984 // expected: 18014398509481985
input: 18014398509481984 + 2 output: 18014398509481984 // expected: 18014398509481986
input: 18014398509481984 + 3 output: 18014398509481984 // expected: 18014398509481987
input: 18014398509481984 + 4 output: 18014398509481988 // expected: 18014398509481988
[2 251 799 813 685 248,4 503 599 627 370 496之间的数字怎么样?
1 . 0000 ---- 0001 * 2^51 => 1 0000 ---- 000.1
|-- 52 bits --| |-- 52 bits --|
二进制值 0.1 正好是 2^-1 (=1/2) (=0.5)
所以当数字小于4,503,599,627,370,496(2^52)时,就有一位可以表示整数的1/2倍 :
input: 4503599627370495.5 output: 4503599627370495.5
input: 4503599627370495.75 output: 4503599627370495.5
小于2,251,799,813,685,248 (2^51)
input: 2251799813685246.75 output: 2251799813685246.8 // expected: 2251799813685246.75
input: 2251799813685246.25 output: 2251799813685246.2 // expected: 2251799813685246.25
input: 2251799813685246.5 output: 2251799813685246.5
/**
Please note that if you try this yourself and, say, log
these numbers to the console, they will get rounded. JavaScript
rounds if the number of digits exceed 17. The value
is internally held correctly:
*/
input: 2251799813685246.25.toString(2)
output: "111111111111111111111111111111111111111111111111110.01"
input: 2251799813685246.75.toString(2)
output: "111111111111111111111111111111111111111111111111110.11"
input: 2251799813685246.78.toString(2)
output: "111111111111111111111111111111111111111111111111110.11"
指数部分的可用范围是多少?格式分配给它的 11 位。
来自Wikipedia(更多详情,去那里)
所以要使指数部分为 2^52,我们正好需要设置 e = 1075。