数字在JS中的表示法
Posted by Mars . Modified at
IEEE 754 双精度数字表示法
数字在JavaScript中按IEEE 754
标准中双精度浮点数(64bit)来表示。一个数占用64位,其格式如下:
用这种形式表示出来的数字,叫做这个数值的原码。
其中的阶码部分:
- 阶码有11位,可表示的数范围是
0 ~ 2047
,这样对应的指数计算结果是2^0 ~ 2^2047
,无法表示很小的小数; - 因为既需要表示很小的小数,又需要表示很大的数,所以标准在这里规定,取
-1023
作为移位值,也就是在阶码原有的基础上需要减去1023才是实际的指数值; - 这样阶码表示的指数计算结果范围:
2^-1023 ~ 2^1024
; - 这样既可以表示非常小的小数,也可以表示较大的数。
尾码部分:
- 因为阶码的存在,由阶码控制小数点的位置;
- 阶码的覆盖范围很广(2^-1023 ~ 2^1024),因此小数点可以前移1023位,后移1024位,远远超出了尾码的位数;
- 因此对于一个小数,如果它的整数位不为1,总可以通过向后移动小数点的方式,将它转化一个整数位为1的数。例如
0.0001001
,可以表示为1.001 * 2^-4
; - 这样设计,可以省出一位尾码,多精确表示一些数字。
特殊的规定
- 当指数部分为
0
(全0),且有效位为0
(全0),表示数字0
; - 当指数部分为
255
(全1),且有效位为0
(全0),表示无穷大或无穷小(取决于符号位); - 当指数部分为
255
(全1),且有效位不为0
(非全0),表示不是一个数,也就是NaN
;
负数的补码表示
为了计算方便,实际保存时:
- 正数表示为原码本身;
- 负数表示为原码的补码。
补码的含义:
原码符号位不变,其余位置按位取反,然后整体+1。
例如,假设我们用8bit表示一个负数,左起第一位表示符号位,则对于-4
:
- 原码表示为:
10000100
; - 符号位不变,其余按位取反:
11111011
; - 整体+1,得到补码:
11111100
。
实际上对于正数a和b,计算机计算a-b
时,是a
的原码与-b
原码的补码相加。例如按上述8bit表示法,计算2-4
:
- 2的原码:
00000010
; - -4的补码:
11111100
; - 计算
2-4
,实际上是2+(-4)
: 结果是11111110
,第一位为1,表示结果负数的补码; - 把它还原成原码:先-1,再除符号位按位取反;
- 得到结果:
10000010
,为正确结果-2
。
可准确表示的整数范围
因为尾数部分只有52位,因此只要整数数值转为2进制后,占位不大于52位,即可准确表示。
52位空间可以表示的整数范围是: 0 ~ 253-1
因此,IEEE754 双精度浮点数,可以精确表示-(2^53 - 1)
到 2^53 - 1
范围的整数。
计算结果精度不够时的处理方式
标准规定,当一个计算结果数的二进制表示,出现尾数位不够时,需要整体向右移动,然后增大阶码。
这个过程中,执行0舍1入
的原则:
- 如果向右移动后移除的部分是
1
,则移动完成后需要+1
; - 如果移除的是
0
,则不需要再进行任何操作。
例如:
0 01111111100 0.1100110011001100110011001100110011001100110011001101
+ 0 01111111100 1.1001100110011001100110011001100110011001100110011010
= 0 01111111100 10.0110011001100110011001100110011001100110011001100111
向右移动后,移除的位为1,则需要补1:
0 01111111101 0011001100110011001100110011001100110011001100110011 1(1 多出,需要舍弃)
最终结果为:
0 01111111101 0011001100110011001100110011001100110011001100110100 (补 1)
执行位运算时,数字不视作IEEE754表示
JS在执行位运算操作时,将数字视为32位二进制串进行操作(同样首位代表正负)。而非上述754表示法。
>>
是带符号右移,表示移动过程中左侧空出来位置用符号位的值来填充(正数补0,负数补1);>>>
是无符号右移,表示移动过程中左侧空出来位置,始终用0
来填充;- 当
>>
或>>>
移动的位数n >= 32
时,先对32取余,再进行移动。,因此a >> 32
与a
始终相等。