前端中涉及金钱计算引发的精度问题

4,319 阅读2分钟

背景

在前端项目中,只要涉及加减乘除计算的时候,就不可避免的遇到精度问题,尤其是涉及到金钱计算时尤为重要,后期项目上线如果这里出了问题可是要扣工资的。下面是我现在负责项目负责到的计算界面:

1635496755(1).jpg

微信截图_20211029164149.png 这里就有金钱和难度系数的计算,都是要进行加减乘除的,所以在开发过程中,我们是无法避免的。

产生问题的原因

这是因为JS中两个数字相加时是以二进制形式进行的,当十进制小数的二进制表示的有限数字超过52位时,在JS里是不能精确储存的,这个时候就存在舍入误差。

Snipaste_2021-10-29_16-50-06.png 下面就跟大家介绍三种方法,最后一种是最为标准的解决方法,供大家参考。

先放大再缩小

既然我们遇到小数计算时才会出现误差,那么我们完全可以先把小数变整数计算之后再变成小数,这样就不会存在精度的问题。

let num1 = 0.1,num2 = 0.2;
console.log((num1*100+num2*100)/100); //0.3

此方法局限就在于需要知道计算数字是几位小数,但是计算结果是多少就是多少完全不会有误差,返回的也是数值类型。

使用自带的toFixed方法

该方法返回 NumberObject 的字符串表示,不采用指数计数法,小数点后有固定的 num 位数字。如果必要,该数字会被舍入,也可以用 0 补足,以便它达到指定的长度。如果 num 大于 le+21,则该方法只调用 NumberObject.toString(),返回采用指数计数法表示的字符串。

let num = 0.1 + 0.2;
let num1  = 0.3 - 0.1;
console.log(num.toFixed(2)) //0.30
console.log(num.toFixed(2)) //0.20

但是其四舍五入的规则与数学中的规则不同,使用的是银行家舍入规则,银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。 具体规则如下:

简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

显然这种规则不符合我们平常在数据中处理的方式。还是会存在误差,这就看贵公司对金钱方面是如何计算的了。

使用标准库Math.js

Math.js 说白了就是一个 JavaScript 的高级数学计算函数库。可以对它进行一层封装用于我们的数字计算,且不会存在误差,里面的方法在这里就不过多赘述,使用前请先安装math.js。

// 封装
import math from 'mathjs';
export default {
    // 加
    add(num1,num2){
        return math.add(math.bignumber(num1),math.bignumber(num2));
    },
    // 减
    subtract(num1,num2){
        return math.subtract(math.bignumber(num1),math.bignumber(num2));
    },
    // 乘
    multiply(num1,num2){
        return math.multiply(math.bignumber(num1),math.bignumber(num2));
    },
    // 除
    divide(num1,num2){
        return math.divide(math.bignumber(num1),math.bignumber(num2));
    },
}


// 使用
import math from './math.js';

let addNum = math.add(0.1,0.2);
let subNum = math.subtract(0.3 - 0.1); 
let mulNum = math.multiply(0.3 * 1.5);
let divNum = math.divide(0.4 / 0.3);