js计算精度不准确问题,想必大家都遇到过。
我们下面来探讨一下。
简单说明一下
问题原因是Javascript采用了IEEE-745浮点数表示法(几乎所有的编程语言都采用),这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024。遗憾的是,我们常用的分数(特别是在金融的计算方面)都是十进制分数1/10,1/100等。二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字。
二进制浮点数表示
0.1 + 0.2 计算过程会转化成二进制,但由于换算为二进制表达时是无穷的。例如:
0.1 -> 0.0001100110011001…(无限)
0.2 -> 0.0011001100110011…(无限)
IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位,所以两者相加之后得到二进制为
0.0100110011001100110011001100110011001100110011001100
因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了 0.30000000000000004。所以在进行算术计算时会产生误差。
既然存在数学计算精度问题,那怎么解决呢?
有种最简单的解决方案,就是给出明确的精度要求,在返回值的过程中,计算机会自动四舍五入,
比如:
var a = 0.1,
b = 0.2;
var c = parsefloat((a + b).toFixed(2));
console.log( c === 0.3); // 打印 true
上面我们把相加的数字,toFixed保留2位小数,结果转换成了字符串,之后用parsefloat把字符串转换成数字。
但是,四舍五入解决不了浮点数的精度问题。比如我们想要的是一个准确计算的结果展示在页面上面,那么18.69*100 = 1869.0000000000002,如果使用四舍五入,结果位18.69.00。显然不是我们想要的结果 1869。
以下是针对加减乘除的整理解决方法,并在项目当中切实验证用到😗***
加法计算
function accAdd(curr, next) {
var curr_l, next_l, m, c;
// 首选获取两个参数小数点后面位数,方便转换成整数。
try {
curr_l = curr.toString().split(".")[1].length;
}
catch (e) {
curr_l = 0;
}
try {
next_l = next.toString().split(".")[1].length;
}
catch (e) {
next_l = 0;
}
c = Math.abs(curr_l - next_l); // 返回一个数字的绝对值,返回差之为正数
m = Math.pow(10, Math.max(curr_l, next_l)); //取10的n次方,取小数点后面位数最大的那个n次方
console.log(c,curr_l,next_l,m)
if (c > 0) { // 如果curr 和 next小数点后面位数不一样
// 计算两个参数,转换为整数之后,位数差。编码两个参数相加,个数和十位数对映不上,从而进位。
var cm = Math.pow(10, c);
/*
* cm 比较curr, next参数小数点后面位数
* 1.如果第一个大于第二个,则第二个参数需要进位
* 2.如果第二个大于第一个,则第一个参数需要进位
* 示例:122.113、110.1 相加,122 与 110 百位、十位、个位一一对应;
* 转换为整数之后分别是 122113、 1101,变成,2113 与 1101 百位、十位、个位对应上了,相加会有问题。
* 此时我们把1101乘以100,变成 110100 与 122113,就满足百位、十位、个位一一对应上了,此时相加问题解决。
* */
if (curr_l > next_l) {
curr = Number(curr.toString().replace(".", ""));
next = Number(next.toString().replace(".", "")) * cm;
} else {
curr = Number(curr.toString().replace(".", "")) * cm;
next = Number(next.toString().replace(".", ""));
}
} else {
curr = Number(curr.toString().replace(".", ""));
next = Number(next.toString().replace(".", ""));
}
// 计算完结果,把整数还原成小数。
return (curr + next) / m;
}
减法计算
function accSub(curr, next) {
var curr_l, next_l, m, c;
try {
curr_l = curr.toString().split(".")[1].length;
}
catch (e) {
curr_l = 0;
}
try {
next_l = next.toString().split(".")[1].length;
}
catch (e) {
next_l = 0;
}
c = Math.abs(curr_l - next_l);
m = Math.pow(10, Math.max(curr_l, next_l));
if (c > 0) {
var cm = Math.pow(10, c);
if (curr_l > next_l) {
curr = Number(curr.toString().replace(".", ""));
next = Number(next.toString().replace(".", "")) * cm;
} else {
curr = Number(curr.toString().replace(".", "")) * cm;
next = Number(next.toString().replace(".", ""));
}
} else {
curr = Number(curr.toString().replace(".", ""));
next = Number(next.toString().replace(".", ""));
}
return (curr- next) / m;
}
乘法计算
function accMul(curr,next){
var m=0,s1=curr.toString(),s2=next.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 accDiv(curr, next) { //去小数点,扩大倍数,再乘以相应倍数
var t1 = 0, t2 = 0, curr_l, next_l;
try {
t1 = curr.toString().split(".")[1].length;
}
catch (e) {
}
try {
t2 = next.toString().split(".")[1].length;
}
catch (e) {
}
curr_l = Number(curr.toString().replace(".", ""));
next_l = Number(next.toString().replace(".", ""));
return (curr_l / next_l) * Math.pow(10, t2 - t1);
}
希望上述内容能让大家看明白, 帮助到正在看文字的朋友。同时也希望看完文字的朋友点个大大的赞。如有错误的地方,还望留言,我及时更正内容。