避免数据相加小数点后产生多位数和计算精度损失的几个见效甚少的方法

94 阅读2分钟

2019021517525428.jpg

JavaScript里精度损失是个非常头疼的毛病,典型的[0.1+0.2==0.30000000000000004]相信大家都有耳闻

这里抛出几个简单的方法,避免一部分0.30000000000000004的头疼情况

/**
 * 加法运算,避免数据相加小数点后产生多位数和计算精度损失。
 * @param num1加数1 | num2加数2
 */
function numAdd(num1, num2) {
	var baseNum, baseNum1, baseNum2;
	try {
		baseNum1 = num1.toString().split(".")[1].length;
	} catch(e) {
		baseNum1 = 0;
	}
	try {
		baseNum2 = num2.toString().split(".")[1].length;
	} catch(e) {
		baseNum2 = 0;
	}
	baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
	return(num1 * baseNum + num2 * baseNum) / baseNum;
};

以上方法的核心思想是 —— 把小数 × 10ⁿ 换算成整数 -> 运算 -> 运算结果 ÷ 10ⁿ 还原数值

看起来毫无破绽的破解方法然并卵,因为我万万没想到

0.55*100==55.00000000000001

0.55/100==0.0055000000000000005

JavaScript 也真是够坑的,连 × 10ⁿ 的运算都不能让人安心

但是!方法还是用的,那就是js的一个替换方法, .replace()

小数转整数的过程,不用运算的方法,而改成处理字符串的方法. 数值转字符串 -> 记录小数点位置 -> 替换小数点[.replace(".", "")] -> 运算 -> 运算结果 ÷ 10ⁿ 还原数值

只要先做好乘法运算,其它3种运算再基于乘法运算,那就可以安全解决问题了

最后把这些内容装起来,就得到下面的公共对象:

var Utils = {
	argAdd: function(arg1, arg2) {
		// 加法函数
		var _this = this,
			r1 = 0,
			r2 = 0,
			m = 0;
		try {
			r1 = arg1.toString().split(".")[1].length
		} catch(e) {}
		try {
			r2 = arg2.toString().split(".")[1].length
		} catch(e) {}
		m = Math.pow(10, Math.max(r1, r2))
		return _this.argDiv((_this.argMul(arg1, m) + _this.argMul(arg2, m)), m)
	},
	argSubtr: function(arg1, arg2) {
		// 减法函数
		var _this = this,
			r1 = 0,
			r2 = 0,
			m = 0;
		try {
			r1 = arg1.toString().split(".")[1].length
		} catch(e) {}
		try {
			r2 = arg2.toString().split(".")[1].length
		} catch(e) {}
		m = Math.pow(10, Math.max(r1, r2));
		return _this.argDiv((_this.argMul(arg1, m) - _this.argMul(arg2, m)), m)
	},
	argMul: function(arg1, arg2) {
		// 乘法函数
		var _this = this,
			m = 0,
			s1 = arg1.toString(),
			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)
	},
	argDiv: function(arg1, arg2) {
		// 除法函数
		var _this = this,
			t1 = 0,
			t2 = 0,
			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(".", ""))
		return _this.argMul((r1 / r2), Math.pow(10, t2 - t1));
	}
}
console.log(Utils.argAdd(0.1, 0.2))
console.log(0.1 + 0.2)
console.log(Utils.argSubtr(0.3, 0.2))
console.log(0.3 - 0.2)
console.log(Utils.argMul(0.55, 100))
console.log(0.55 * 100)
console.log(Utils.argDiv(0.55, 100))
console.log(0.55 / 100)

一句话总结:不要和小数玩运算

参考资料:JS 四则运算(加减乘除小数运算)避免损失精度

end