|
我们都知道0.1 + 0.2 = 0.3,但是在JavaScript中0.1加0.2得到的不是0.3,而是 0.30000000000000004。由于这种微小的舍入错误,导致很难测试特定的浮点值。比如下面的例子:
let a = 0.1
let b = 0.2
if (a + b == 0.3) {
console.log("You got 0.3.")
}
这里检测两个数值之和是否等于0.3。如果两个数值分别是0.05和0.25,或者0.15和0.15,那没问题。但如果是0.1和0.2,如前所述,测试将失败。因此永远不要测试某个特定的浮点值。
let a = 0.05
let b = 0.25
if (a + b == 0.3) {
console.log("You got 0.3.")
}
之所以存在这种舍入错误,是因为使用了 IEEE 754 数值,这种错误并非ECMAScript所独有。其他使用相同格式的语言也有这个问题。  在这之前,我们先来了解一个小概念,为了实现计算,数字是如何表示的,极小数和极大数通常使用科学计数法来表示,例如,0.0006606用科学计数法的标准化写法为:6.606 x 10^-4。
- Sign:用来表示数字的正负,
0为正数,1为负数 - Exponent:这里用来存储科学计数法的指数,也就是
n次方 - Mantissa:这里用来存储科学计数法前面的数值
这里用0.125作为示范,转换为二进制,用的是乘二取整,只计算小数位,从上至下得到0.001。
0.125 x 2 = 0.25,整数部分是00.25 x 2 = 0.5,整数部分是00.5 x 2 = 1.0,整数部分是1
接下来,我们把0.1转换为二进制。
0.1 x 2 = 0.2,整数部分是00.2 x 2 = 0.4,整数部分是00.4 x 2 = 0.8,整数部分是00.8 x 2 = 1.6,整数部分是10.6 x 2 = 1.2,整数部分是10.2 × 2 = 0.4,整数部分是0
这里我们发现无法进行整除,循环0110,那么十进制的0.2也是会有同样的结果,因为我们可以从上面的结果里面看到从0.2开始进行乘2就一样会得到一个无限循环,循环的部分依旧是0110。那么肯定会出现后续的位置无法存储进去,这样就迫使计算机取一个近似的数字。
我们知道3个1/3相加可以得到3/3也就是1,不过这是十进制,就类似于我们把1除以3得到的0.333的无限循环以后再进行相加,很明显这些无限循环的0.333相加怎么也没有办法得到一个完整的1。
解决方法: 因为小数乘二取整会有无限循环的情况,但是整数除二取余是不会的,所以整数部分不会出现精度丢失问题。
let a = 0.1
let b = 0.2
if ((a * 10 + b * 10) / 10 == 0.3) {
console.log("You got 0.3.")
}
|