消除JS计算误差问题

使用背景

JS进行加减乘除计算时,经常会遇到计算结果不准确的情况,如:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解决方法

1. toFixed()

该方法遵循四舍五入的原则。当四舍五入可以满足系统需要时,可使用;但如果对数字精度要求高,则不符合要求。

2. JavaScript 数字处理的第三方库:big.js(big.js文档跳转

big.js和bignumber.js、decimal.js都是同一个作者写的,都是用于JS的数字处理,但简单的消除加减乘除的计算误差使用big.js即可满足;
包的大小:big.js < bignumber.js < decimal.js;
bignumber.js 和 decimal.js 存储值的进制比 big.js 更高,因此当操作大量数字时,前两者的速度会更快。

big.js的使用

a. 使用npm安装依赖:npm install big.js
b. 在使用时引入:import Big from 'big.js';
c. 加法
注:最后的结果需要使用toString()转成字符串才可以正常显示,此时使用typeof数据类型是string,要转换成数字则可以使用Number(),此时使用typeof数据类型是number

<script>
	const x = new Big(0.1);// 0.1
    const y = x.plus(0.2); // 0.1 + 0.2
    const z = Big(0.7).plus(x).plus(y); // 0.7 + 0.1 + 0.3
    console.log(0.1 + 0.2);// 0.30000000000000004
    console.log(0.7 + 0.1 + 0.3);// 1.0999999999999999
    console.log(y.toString());// '0.3'
    console.log(typeof (y.toString()));// string
    console.log(Number(y.toString()));// 0.3
    console.log(typeof (Number(y.toString())));// number
    console.log(z.toString());// '1.1'
</script>

d. 减法

<script>
	 const x = new Big(0.3);
     const y = x.minus(0.1); // 0.3 - 0.1
     const z = Big(0.7).minus(x).minus(y); // 0.7 - 0.3 - 0.2
     console.log(0.3 - 0.1);// 0.19999999999999998
     console.log(y.toString());// '0.2'
     console.log(z.toString());// '0.2'
     console.log(typeof (z.toString()));// string
     console.log(Number(z.toString()));// 0.2
     console.log(typeof (Number(z.toString())));//number
</script>

e. 乘法
注意:位数太多或计算式中有科学计数法表示的数字,则结果会自动换算成科学计数法,若不想显示科学计数法需要自行转换

<script>
	const x = new Big(0.1);
    const y = x.times(0.2); // 0.1 * 0.2
    const m = Big('7e+500').times(x).times(y); // '7e+500' * 0.1 * 0.02
    const z = Big(0.05).times(x).times(y); // 0.05 * 0.1 * 0.02  
    console.log(0.1 * 0.2);// 0.020000000000000004
    console.log(y.toString());// '0.02'
    console.log(m.toString());// '1e-16'
    console.log(z.toString());// '0.0001'
    console.log(typeof (z.toString()));// string
    console.log(Number(z.toString()));// 0.0001
    console.log(typeof (Number(z.toString())));// number
</script>

f. 除法

<script>
	let x = new Big(0.3);
	let y = x.div(0.2); // 0.3 / 0.2
	let z = x.div(y).div(0.08); // 0.3 / 1.5 / 0.08
	console.log(0.3 / 0.2);// 1.4999999999999998
	console.log(y.toString());// '1.5'
	console.log(z.toString());// '2.5'
	console.log(typeof (z.toString()));// string
	console.log(Number(z.toString()));// 2.5
	console.log(typeof (Number(z.toString())));// number
</script>

g. big.js还可以进行比较大小、取绝对值、取模、四舍五入等运算,基本满足普通计算需求,详见big.js文档

补充:科学计数法转换成普通数字格式

<script>
getFullNum(num) {
	// 处理非数字
	if(isNaN(num)) { return num; };
	// 处理不需要转换的数字
	var str = '' + num;
	if(!/e/i.test(str)) { return num; };
	return (num).toFixed(18).replace(/\.?0+$/, '');
}
let num = 0.0000000000000003;
let result = this.getFullNum(num);//调用方法
console.log(0.0000000000000003);// 3e-16
console.log(result);// 0.0000000000000003
</script>

3. 数字升级

对符号两边要计算的数字先全部转化成整数(即升级,如乘以10的n次幂),再做对应的运算,对结果再进行降级(除以10的n次幂),得到最终的结果。

原理:整数的运算不会有误差

a. 加法和减法

<script>
	// 消除js计算误差:add是加数(减数),reduce是被加数(被减数),s是加减符号,n是指数
	formatNum(add, reduce, s, num) {
    	let m = Math.pow(10, num); // m是升级/降级的数
    	let res = s == '+' ? (add * m + reduce * m) / m : (add * m - reduce * m) / m;
    	return (res * m) / m;
	}
	console.log(this.formatNum(0.1, 0.2, '+', 10))
	console.log(this.formatNum(0.3, 0.2, '-', 10))
</script>

b. 乘法

<script>
	// arg1乘数, arg2被乘数
	accMul(arg1, arg2) {
	     let m = 0
	     let s1 = arg1.toString();//乘数转成字符串
	     let s2 = arg2.toString();//被乘数转成字符串
	     try { m += s1.split('.')[1].length; } catch (e) { }//计算小数点后的位数,用于升/降级
	     try { m += s2.split('.')[1].length; } catch (e) { }//计算小数点后的位数,用于升/降级
	     return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);//去掉小数点,相乘,然后降级相应的数量
	},
	console.log(this.accMul(0.2, 0.11))
</script>

c. 除法

<script>
	accDiv(arg1,arg2) {
	    let t1 = 0;
	    let t2 = 0;
	    let r1,r2;
	    try{ t1 = arg1.toString().split('.')[1].length; }catch(e) {}//
	    try{ t2 = arg2.toString().split('.')[1].length; }catch(e) {}
	    r1 = Number(arg1.toString().replace('.',''));
	    r2 = Number(arg2.toString().replace('.',''));//去掉小数点
	    if (t2 - t1 > 0) {
	        return (r1 / r2) * Math.pow(10,t2 - t1);
	    }else{
	        return (r1 / r2) / Math.pow(10,t1 - t2);
	    }
	}
	console.log(this.accDiv(0.03, 0.2))
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值