Number类型二进制表示法
问你一个问题:JS
中 Number
类型数据总共有多少个?
这有答案吗?
《Ecma-262 Edition 5.1》给出的答案是:18437736874454810627,即 $2^{64}-2^{53}+3$。
那么,具体是怎么算出来的??
本文主要解决这个问题。
规范上说,Number
类型使用的是《
IEEE二进制浮点数算术标准(IEEE 754)》的双精度 64 位格式。
其核心问题:是怎么用 64 个比特位,来表示一个具体浮点类型数值?
《IEEE 754.pdf》把这 64 位比特分成 3 部分:
其中,s
表示符号位,占 1 比特;e
表示指数位,占 11 比特;f
表示分数位,占 52 比特。
规范里详细地给出了各种情况所对应的值:
上述分类是符合 MECE 原则的,不重复且不遗漏。
其中 e
占 11 位,因此该部分能表示的数字范围是 0 至 2047,即,0 至$2^{11}-1$。分类首先标准就是看 e
是否位于边界。
前两条是指当 e
部分都填充二进制 1 时,即当指数部分为最大值时,表示 NaN
和正负 Infinity
这两种情况。
具体来说,第一种,当小数部分同时不为零时,此时表示 NaN
。JS
中的字面量 NaN
,正是对应这种情况,另外要求 NaN
始终不能等于自身。
第二种,而当 f
部分为 0 时,根据符号位的正负,来表示相应的正负无穷。
第三条是指当 e
在介于最小值和最大值之间的情形。此时被称为规范化数字,有别于下面的第四条。这里值的确定方式是 $(-1)^s2^{e-1023}(1·f)$。此时要注意 $(1·f)$ 指的是二进制小数。
举个例子,假设某个数字的 64 位比特表示如下: 1|10000000001|110000...
,此时 s
是 1, e
是 $2^{10}+2^0$,即 1025。那么该数据对应的值 v
是:$(-1)2^{2}(1·11)$ 而二进制小数 $(1·11)$ 其值是$2^0+2^{-1}+2^{-2}$,即 1.75
。因此该表示是 -7。
第四条是指非规范化数字情形。此时数字已经非常小了。注意这里是二进制小数是 $(0·f)$ 不同于第三条里的 $(1·f)$。最后一类是正负 0 的情形。
我们已经把规范过了一遍,现在,我们来算算开头的问题:Number
类型共有多少个值?
从上述分类中可以看出:后三类是正常值,前两类总共有 $212^{52}$ 种可能(排列组合原理,s
部分为 2047,因此相应的只有一种可能),这么多种可能,却在 JS
中只表示 NaN
、+Infinity
、-Infinity
这 3 个值。因此总数 $2^{64}-2^{53}+3$。
至此,本文接近尾声了。
最后提醒一下,阅读《Ecma-262 Edition 5.1》8.5节不要犯迷糊:
The 18437736874454810622 (that is, $2^{64}−2^{53}−2$) finite nonzero values are of two kinds: 18428729675200069632 (that is, $2^{64}−2^{54}$)of them are normalised, having the form
$s × m × 2^e$
where s is +1 or −1, m is a positive integer less than $2^{53}$ but not less than $2^{52}$, and e is an integer ranging from −1074 to 971, inclusive. The remaining 9007199254740990 (that is, $2^{53}−2$) values are denormalised, having the form
$s × m × 2^e$
where s is +1 or −1, m is a positive integer less than $2^{52}$, and e is −1074.
其中提到了 e
是从 -1070 到 971,这两个数字怎么来的?其实它与《IEEE 754.pdf》说的是同一回事,比如规范化情形,只是从指数部分里拿出了 $2^{52}$ 放到了 m
里面。 m
等价于$2^{52}(1·f)$。
本文完。