JS数字存储
存储标准
JavaScript中的数字的存储标准是IEEE754浮点数标准。代码中使用的无论是整数还是小数,都是同一种数据类型——64位双精度浮点型。
64位存储划分
64位分别是1位符号位,11位指数位,52位尾数位。
符号位
计算机只能识别0和1,(-1)^x, x代表计算机存储的符号位,所以可得出0代表正,1代表负。
指数位
11位指数位理论上存储的最大值是11111111111(11个1),所代表的数字就是2^11-1 = 2047。而指数也有正负之分,所以取0-2047一个中间数1023作为分界,0-1022为负,1024-2047为正,因此指数范围[-1023, 1024],可以表示一个数字的范围
尾数位
首先将数字转换为科学记数法,科学记数法小数点之前一直是1,所以不用占位,小数部分即为尾数位,表示该数字的精度。
实际值计算公式
V = (-1)^S * 2^(E-1023) * M
S:符号位存值
E:指数位存值
M:尾数位存值
举例:存储8.8125
十转换转换二进制
整数位: 8 转化为 1000
方法:(依次除以2,位数为除以2的次数,值为余数部分,直到商为1)
8 除以 2 等于 4 余数为 0,所以第一位为 0,
4 除以 2 等于 2 余数为 0,所以第二位为 0,
2 除以 2 等于 1 余数为 0,所以第三位为 0,
1 除以 2 等于 0 余数为 1,所以第四位为 1,
小数位:0.8125转化为0.1101
方法:(小数部分依次乘2,位数为乘2的次数,值为整数部分,直到积不含小数)
0.8125 乘 2 等于 1.625,所以第一位为1,
0.625 乘 2 等于 1.25, 所以第二位为1,
0.25 乘 2 等于 0.5, 所以第三位为0,
0.5 乘 2 等于 1, 所以第四位为1
8.125 转化为二进制位 1000.1101
转化为科学记数法
1000.1101 = 1.0001101 * 2^3
代入公式求值
很明显此值为正,所以S = 0;
指数位E – 1023 = 3,所以E = 1026转化为二进制为:10000000010
尾数位为:0001101
最终结果
0 10000000010 0001101000000000000000000000000000000000000000000000
存储的最大安全整数
我们都应该或多或少知道js有个最大安全整数,这个值是2^53-1,为 9007199254740991,在这个范围(-2^53-1, 2^53-1)之内,所有的整数都有唯一的浮点数表示,这就叫安全整数。
JS所能表示的最大数
由上面的指数位和位数位,我们应该很容易得出来结论,JS所能表示的最大数应该是:
0111111111111111111111111111111111111111111111111111111111111111(1个0,63个1),这个数字肯定远远大于最大安全整数。
不知你是否有相同的疑问,最大安全整数范围怎么来的?为什么超过这个范围会不安全?
那我们现在就根据最大安全整数找寻下为什么
先记录下最大安全整数:9007199254740991
2^53 - 1 = 11111111111111111111111111111111111111111111111111111 (53个1),转换为科学记数法,1.1111111111111111111111111111111111111111111111111111再存储发现数字不会发生变化,因为尾数正好是52位,恰好可以完美存储。
2^53 = 100000000000000000000000000000000000000000000000000000 (53个0),转换成科学记数法,因为后面都是0,存储起来看着也没啥问题
2^53 + 1 = 100000000000000000000000000000000000000000000000000001 (中间52个0),这时候转换为科学记数法为1.00000000000000000000000000000000000000000000000000001 * 2^53,尾数为为小数点后面数位,此时为53位,而js只能存储52位,所以会发生近似取值,0舍1进,所以存储的尾数为0000000000000000000000000000000000000000000000000001(51个0)取出在换成科学记数法1.0000000000000000000000000000000000000000000000000001 * 2^53
接下来我们将原始值和经过计算机存储的值拿出来进行比较:
原始:100000000000000000000000000000000000000000000000000001
存储:100000000000000000000000000000000000000000000000000010
没存储的值 = 2^53 + 1
存储过的值 = 2^53 + 2
这样经过计算机存储,值就不再一一对应。
自己可以测试
Math.pow(2, 53) = 9007199254740992
Math.pow(2, 53) - 1 = 9007199254740991
Math.pow(2, 53) +1 = 9007199254740992
Math.pow(2, 53) + 2 = 9007199254740994
至于Math.pow(2, 53) +1的值为什么不等于Math.pow(2, 53) + 2 而是等于Math.pow(2, 53),和上述计算结果不一致,这个我没搞明白,欢迎知道的在下方评论!
版权声明:本文为lx199509原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。