js浮点数计算误差bug

最近在做项目测试时,用到了JS浮点数计算,0.2+0.1结果为0.30000000000000004。因为都是取一位小数,就把计算后面加了个toFixed(1).虽然解决了问题,但还是详细看了下其中缘由。

计算机中的浮点数:

浮点指的是带有小数的数值,浮点运算即是小数的四则运算,常用来测量电脑运算速度。大部份计算机采用二進制(b=2)的表示方法。位(bit)是衡量浮点数所需存储空间的单位,通常为32位或64位,分别被叫作单精度和双精度。

误差现象:
console.log(0.1+0.2);    // 输出 0.30000000000000004console.log(0.15+0.15);  // 输出 0.3
console.log(0.7+0.1);    // 输出 0.7999999999999999console.log(0.6+0.2);    // 输出 0.8
console.log(0.3*3);      // 输出 0.8999999999999999console.log(3*3/10);     // 输出 0.9

从以上现象可以看出,并不是所有的浮点数运算都会出问题,只是部分。

0.7+0.1 输出有偏差0.6+0.2 输出正确

具体是什么原因呢?

误差原因:

不仅在 JavaScript 中存在这个「问题」,所有的支持二进制浮点数运算(绝大部分都是 IEEE 754[1] 的实现)的系统都存在这个现象。其原因就是,在有限的存储空间下,绝大部分的十进制小数都不能用二进制浮点数来精确表示。例如,0.1 这个简单的十进制小数就不能用二进制浮点数来表示。所谓「计算机浮点数」,其实就是二进制的「科学计数法」。在十进制中,科学计数法的形式是:相应的,二进制的科学计数法就是:而在有限的存储空间下,十进制小数 0.1 无论如何也不能用这种形式来表示,因此,计算机在存储它时,产生了精度丢失,所以就出现了问题中所描述的现象。二进制浮点数具体的储存、运算细节,可以查阅现在应用最广的 IEEE 754。

解决方案:

如何解决呢?看这个例子:
console.log(0.3*3);      // 输出 0.8999999999999999console.log(3*3/10);     // 输出 0.9

浮点数计算有问题,整数计算是没问题的,那么将浮点转化成正数运算,之后再切回浮点就可以保证没有偏差了

注:不推荐js做太多的浮点运算。

以下方法可以帮助你修正浮点运算的偏差:

// 浮点数求和function add(a, b) {    var c, d, e;
    try {
        c = a.toString().split(".")[1].length;
    } catch (f) {
        c = 0;
    }
    try {        d = b.toString().split(".")[1].length;
    } catch (f) {        d = 0;
    }    return e = Math.pow(10, Math.max(c, d)), (mul(a, e) + mul(b, e)) / e;
} 
// 浮点数相减function sub(a, b) {    var c, d, e;
    try {
        c = a.toString().split(".")[1].length;
    } catch (f) {
        c = 0;
    }
    try {        d = b.toString().split(".")[1].length;
    } catch (f) {        d = 0;
    }    return e = Math.pow(10, Math.max(c, d)), (mul(a, e) - mul(b, e)) / e;
} 
// 浮点数相乘function mul(a, b) {    var c = 0,        d = a.toString(),        e = b.toString();
    try {
        c += d.split(".")[1].length;
    } catch (f) {}
    try {
        c += e.split(".")[1].length;
    } catch (f) {}    return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
} 
// 浮点数相除function div(a, b) {    var c, d, e = 0,
        f = 0;
    try {        e = a.toString().split(".")[1].length;
    } catch (g) {}
    try {
        f = b.toString().split(".")[1].length;
    } catch (g) {}    return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), mul(c / d, Math.pow(10, f - e));
}

验证:

console.log(0.1+0.2);        // 输出 0.30000000000000004console.log(add(0.1, 0.2));  // 输出 0.3
console.log(0.7+0.1);        // 输出 0.7999999999999999console.log(add(0.7, 0.1));  // 输出 0.8
console.log(0.3*3);          // 输出 0.8999999999999999console.log(mul(0.3, 3));    // 输出 0.9

采用优化方法后计算的结果都是正确的。