问题提出
1 | 10.23 + 0.2345; |
10.23 + 0.2345 实际测试结果为10.464500000000001
,而不是10.4645
0.1 +0.2 实际测试结果为0.30000000000000004
,而不是0.3
原因
浮点数在 2 进制中存储的时候,无法精确存储
js 在数据存储中采用 64 位双精度浮点数形式储存
整数转为 2 进制
用 8 位 2 进制表达 10 进制下的数字 8
1 | 8/2 = 4 => 0 |
商为 0 终止计算,然后倒排为 1000,因为是 8 位,所以在上 4 位补 0
表达即为:00001000
浮点数转换为 2 进制
例如 0.82 的二进制表达为:
1 | 0.82 _ 2 = 1.64 => 1 |
表达即为:0.11010001…
举例:10 进制下 0.1 表达为二进制下为:0.000110011…
浮点数在 64 位双精度下的存储
符号位 S:第 1 位是正负数符号位(sign),0 代表正数,1 代表负数
指数位 E:中间的 11 位存储指数(exponent),用来表示次方数
尾数位 M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零
公式表示:
以上的公式遵循科学计数法的规范,在十进制是为 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
0.1 转成二进制表示为 0.0001100110011001100(1100 循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的 1,得到 100110011…
0.1 +0.2
1 | // 0.1 和 0.2 都转化成二进制后再进行运算 |
生产环境中的解决办法
https://github.com/dt-fe/number-precision