浮点数的存储
首先要搞清楚javascript如何存储小数的,它和其他语言存储,javascript中的整数和小数都只有一种类型--number
,它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数。
格式
(1)sign bit(符号): 第1位用来表示正负号,0代表数值为正,1代表数值为负。
(2)exponent(指数): 中间的11位用来表示次方数。
(3)mantissa(尾数):最后的52位用来表示精确度,在二进制的“科学记号”,数字被表示为:Mantissa*2^exponent,超出的部分自动进一舍零。
实际数字就可以用以下公式来计算:
注意以上的公式遵循科学计数法的规范,在十进制是为0<M<10,到二进行就是0<M<2。也就是说整数部分只能是1,所以可以被舍去,只保留后面的小数部分。如 4.5 转换成二进制就是 100.1,科学计数法表示是 1.001*2^2,舍去1后 M = 001。E是一个无符号整数,因为长度是11位,取值范围是 0~2047。但是科学计数法中的指数是可以为负数的,所以再减去一个中间数 1023,[0,1022]表示为负,[1024,2047] 表示为正。如4.5 的指数E = 1025,尾数M为 001。
最终的公式变成:
所以 4.5 最终表示为(M=001、E=1025):
下面再以 0.1 例解释浮点误差的原因, 0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的1,得到 100110011...。最终就是:
转化成十进制后为 0.100000000000000005551115123126,因此就出现了浮点误差。
为什么 0.1+0.2=0.30000000000000004?
计算步骤为:
// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111
// 转成十进制正好是 0.30000000000000004