双精度数的存储方法
js中不区分整数和浮点数,统一按双精度浮点数存储(就是c++里的double)。使用64位固定长度
- 符号位S : 第一位 表示正负,0正
- 指数位E: 中间的11位
- 尾数位M:后面的52位
公式遵循科学计数法规范,十进制中M∈(0,10) 二进制就(0,2),所以M的整数部分只能是1,可以舍去省下一位。 那么4.5 二进制100.1 科学计数法位 1.001*2^2 则s=0,m=001,e= 2(e的二进制表示下面解释一下)。
指数有正负,E表示的二进制在(0-2^11)之间 因此就就取中间值 1023 为 0 。 1024 则为1,1022为-1。
大数溢出的问题
可以看出来M只有52位,所以一直到2^52 -1 都不会有溢出,即 1.111(.后52个1)*2^52。
然后+1成2^53, 其实M位已经开始溢出了一位0,1.00(.后52个0)*2^53 。 但是因为进位后舍弃的是0,并未影响2^53的准确度。 这时候就比较明朗了,2^53+1 最后的一位1依然被舍弃, 所以 2^53+1= 2^53。 2^53+2 则又正常。
可以得出个规律:
- (2^53, 2^54)之间的数 每两个精确一个,而且是只有偶数可以精确
- (2^54, 2^55)之间的数 每4个精确一个,而且是只有4的倍数可以精确
- …… 以2的更高次幂的继续跳
浮点误差
0.1===0.10000000000000001
//true
0.1===0.1000000000000001
//false
0.1 转换为二进制后,0.00011001100……本身就是无限循环的。这个例子其实还是受限于M的位数, 2^53 约等于10^16. 所以0.1000000000000001十六位的时候仍然正确判断,十七位就溢出 认为他们相等了。
0.1+0.2
//0.30000000000000004
// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111
// 转成十进制正好是 0.30000000000000004
上面讲到0.1转为二进制后,0.00011001100……是1100无限循环,而js只能有53位的尾数存储,所以其实存储的0.1 是接近0.1000000000000000055511的数
那么 为什么会出现下面的情况
0.1
//0.1
其实就是第一个例子下讲到的2^53 约等于10^16,所以以十进制表示的时候,最多就16位,多余的部分就舍去。
0.1.toPrecision(22)
//0.1000000000000000055511
赞~