前端开发时js小数运算容易出问题的处理心得

1,266 阅读2分钟

js小数运算异常处理

异常:

小数运算相乘出现无限循环异常

    let result 
    result = 3*2.8;
    console.log('result:',result);

异常原因:

JavaScript 里的数字是采用 IEEE 754 标准的 64 位双精度浮点数。该规范定义了浮点数的格式,对于64位的浮点数在内存中的表示,最高的1位是符号位,接着的11位是指数,剩下的52位为有效数字,具体:

第0位:符号位, s 表示 ,0表示正数,1表示负数;

第1位到第11位:储存指数部分, e 表示 ;

第12位到第63位:储存小数部分(即有效数字),f 表示。

十进制的小数如0.1和0.2等都会被转换成二进制,但由于浮点数用二进制表达时是无穷的,所以相加时会出错。

0.1 -> 0.0001100110011001101...(无限)
0.2 -> 0.00110011001100110011...(无限)
0.5 -> 0.1(有限)
2.8 -> 10.11001100110011001101...(无限)

异常得到缓解:

经过放大小数后在放大倍数倍率内的小数正常,但是在倍率外的还是会出现无限循环异常

    let result 
    result = 3*(2.8* 1000000000)/1000000000;
    console.log('result:',result);

解决异常的推理过程:

运算出错可能是类型层级的错误,试着转化类型。使用toFixed()处理,但只是使用toFixed()反而还引入了相加时当做字符串相加的新问题

    let result 
    result = (3*(2.8* 1000000000).toFixed()+(2345* 1000000000).toFixed())/1000000000;
    console.log('result:',result);

问题解决:

使用toFixed()处理后再加上Number()进行数字类型转化,无限循环小数问题虽然解决了,但是这种转化操作是去小数的,所以精度还是取决于相乘倍数

    let result 
    result = (3*Number((2.8* 1000000000).toFixed())+Number((2345* 1000000000).toFixed()))/1000000000;
    console.log('result:',result);

特殊情况:

除此之外还发现如果是在数组中的小数运算是没有这个异常的,估计是数组的运算中包含自动的类型转化,可以去除此问题,所以运算异常在数组运算时不体现

    let numberArray = [2.8,1.0000001,3.00000000000002]
    let count = 0;
    for (let k = 0; k < numberArray.length; k++) {
      count += numberArray[k];
    }
    const result = count;
    console.log('result:',result);