先附上代码,后面有原因分析及解决方案,感兴趣可以自行查看:
//加法
function Add(arg1, arg2) {
var r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (Mul(arg1, m) + Mul(arg2, m)) / m;
}
//减法
function Sub(arg1, arg2) {
var r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (Mul(arg1, m) - Mul(arg2, m)) / m;
}
//乘法
function Mul(arg1, arg2) {
var 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)
);
}
function Div(arg1, arg2) {
var t1 = 0,
t2 = 0,
r1,
r2;
try {
t1 = arg1.toString().split(".")[1].length;
} catch (e) {}
try {
t2 = arg2.toString().split(".")[1].length;
} catch (e) {}
with(Math) {
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
}
问题描述:
在做公司库存数量的逻辑时踩到的大坑,如果对于浮点数,直接在JavaScript中进行加减乘除四则运算会出现很大的误差。问题产生的原因在于,计算机中是将十进制的数据转化为二进制来进行计算的,而对于浮点型的数据,转化成二进制之后可能会变成一个无限循环的数字,而计算机中是不允许无限循环的存在的,那就要做截断,而截断后的数据运算之后再转成十进制就可能会产生极大的误差。
其实并不是只JS存在这种问题,Java、C++也都如此,只不过他们内部都有封装好的方法来解决这个问题。JS是一门弱语言,没有做类似的操作。
原因分析:
浮点数是相对于定点数来说的。
计算机中小数的表示法,有定点和浮点两种。
定点,即小数点固定,比如:302876512411.25
小数点固定在数字的个位数右边
浮点,即小数点不固定(浮动),3.028e+11
小数点不固定在个位数和小数之间,而是根据指数值进行前后浮动
可以这么理解,科学记数法就是浮点数的表示方式
那么,我们为什么要使用浮点数呢?
我们可以先考虑下,为什么要使用科学记数法?
302876512411.25
3.028e+11
科学记数法的核心就是:通过移动小数点,只在小数点前保留一位数字,其他都算作小数,并使用指数记录小数点移动的位数
好处是,通过指数就可以很直观的看出数值的大小(而不用个十百千万的数)
其实它还有另一个好处,如果省略小数点后几位的话,它会显得很廋,不像原值那么臃肿(缺点是精度丢失)
解决方案:
看到网上也有用toFixed()方法来解决的,但是这种四舍五入的方式在高精度需求下也显得不那么严谨。
最常用的方法就是把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂),这是大部分编程语言处理精度差异的通用方法。
欢迎留言交流,QQ:1960816818