还在为js计算精度缺失迷茫?简单粗暴教你一招!

大多数语言在处理浮点数的时候都会遇到精度问题,但是在JS里似乎特别严重,比如:
console.log(1.1-0.5);
结果居然是0.6000000000000001,加减乘除之类的都会有这个问题。

那这是js的错误吗?

当然不是,你的电脑做着正确的二进制浮点运算,但问题是你输入的是十进制的数,电脑以二进制运算,这两者并不是总是转化那么好的,有时候会得到正确的结果,但有时候就不那么幸运了。

alert(0.7+0.1);//输出0.7999999999999999
alert(0.6+0.2);//输出0.8

你输入两个十进制数,转化为二进制运算过后再转化回来,在转化过程中就会有损失。

但一般的损失往往在乘除运算中比较多,而JS在简单的加减法里也会出现这类问题,这个误差虽然非常小,但却是不该出现的。

有一种比较简单的逻辑,比如0.7+0.1,先把0.1和0.7都乘10,加完之后再除10。

按照这个逻辑,我们可以对加减乘除进行封装如下:

		(function () {
            var calc = {

                /*
                函数,加法函数,用来得到精确的加法结果 
                说明:函数返回较为精确的加法结果
                参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
                调用:Calc.Add(arg1,arg2,d) 
                返回值:两数相加的结果,返回类型为string
                */
                Add: function (arg1, arg2, d) {
                    // 将传入的arg1和arg2转为字符串再进行切割,获取小数点的位数
                    let arg1Arr = arg1.toString().split("."), arg2Arr = arg2.toString().split("."), d1 = arg1Arr.length == 2 ? arg1Arr[1] : "", d2 = arg2Arr.length == 2 ? arg2Arr[1] : ""
                    d = typeof d === "number" ? parseInt(d) : Math.max(d1.length, d2.length) // 保留的小数位数等于 传入的d 或者 两个数的小数点后位数
                    let m = Math.pow(10, d) // 取10的maxLen次方
                    return Number(((arg1 * m + arg2 * m) / m).toFixed(d)) // 判断传入的d是否为number类型,是的话进行截取小数点后d位,不传就是undefined。
                },

                /*
                函数:减法函数,用来得到精确的减法结果 
                说明:函数返回较为精确的减法结果
                参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
                调用:Calc.Sub(arg1,arg2,d) 
                返回值:两数相减的结果
                */
                Sub: function (arg1, arg2, d) {
                    return Calc.Add(arg1, -Number(arg2), d)
                },

                /*
                函数:乘法函数,用来得到精确的乘法结果 
                说明:函数返回较为精确的乘法结果。
                参数:arg1:第一个乘数;arg2第二个乘数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
                调用:Calc.Mul(arg1,arg2,d) 
                返回值:两数相乘的结果 

                */
                Mul: function (arg1, arg2, d) {
                    d = typeof d === "number" ? parseInt(d) : (arg1.toString().split(".")[1] ? arg1.toString().split(".")[1].length : 0) + (arg2.toString().split(".")[1] ? arg2.toString().split(".")[1].length : 0)
                    return Number(arg1.toString().replace(".", "")) * Number(arg2.toString().replace(".", "")) / Math.pow(10, d) 
                },

                /*
                函数:除法函数,用来得到精确的除法结果
                说明:函数返回较为精确的除法结果。
                参数:arg1:除数;arg2被除数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
                调用:Calc.Div(arg1,arg2,d) 
                返回值:arg1除于arg2的结果
                */
                Div: function (arg1, arg2, d) {
                    d = typeof d === "number" ? parseInt(d) : (arg2.toString().split(".")[1] ? arg2.toString().split(".")[1].length : 0) - (arg1.toString().split(".")[1] ? arg1.toString().split(".")[1].length : 0)
                    return Number(arg1.toString().replace(".", "")) / Number(arg2.toString().replace(".", "")) * Math.pow(10, d)
                }
            };
            window.Calc = calc;
        }());

该方法返回值是string类型,是因为:

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

参考文章:
https://www.iteye.com/blog/talentluke-1767138
https://blog.csdn.net/baidu_29701003/article/details/88103436
https://blog.csdn.net/yexuan14/article/details/84561788

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值