本文主要对javascript避免数字计算精度误差的方法进行介绍,希望对大家有所帮助。 0.1 + 0.2 等于几?0.3 那还用问吗?但是同样的问题放在编程语言中,或许就不是想象中那么简单的事儿了。
不信?我们可以做个试验。先来看一段 JS。
var num1 = 0.1; var num2 = 0.2; alert(num1+num2 === '0.3');
执行结果是 false。没错,当我第一次看到这段代码时,我也理所当然地以为它是 true,但是执行结果让我大跌眼镜,是我的打开方式不对吗?非也非也。我们再执行以下代码试试就知道结果为什么是 false 了。
var num1 = 0.1; var num2 = 0.2; alert( num1+numB );
原来,0.1 + 0.2 = 0.30000000000000004。是不是很奇葩?其实对于浮点数的四则运算,几乎所有的编程语言都会有类似精度误差的问题,只不过在 C++/C#/Java 这些语言中已经封装好了方法来避免精度的问题,
而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出。下面就分析下为什么会有这个精度误差,以及怎样修复这个误差。
首先,我们要站在计算机的角度思考 0.1 + 0.2 这个看似小儿科的问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制看看:
0.1==》0.1.toString(2)==》0.0001100110011(无限循环..)
0.2==》0.2.toString(2)==》0.001100110011(无限循环..)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100110011001100 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。
原来如此,那怎么解决这个问题呢?我想要的结果就是 0.1 + 0.2 === 0.3 啊!!!
有种最简单的解决方案,就是给出明确的精度要求,在返回值的过程中,计算机会自动四舍五入,
比如:
var num1 = 0.1; var num2 = 0.2; alert( parseFloat((num1 + num2).toFixed(2)) === 0.30 );
但是四舍五入明显不是一劳永逸的办法。
如果有一个方法能帮我们解决这些浮点数的精度问题,那该多好!所以,我们就自己写一个方法。
我们来试试下面这个方法:
formatNum = function(f, digit) { var m = Math.pow(10, digit); return parseInt(f * m, 10) / m; }
var num1 = 0.1;
var num2 = 0.2;
alert(formatNum(num1 + num2, 1) === 0.3);
这个方法是什么意思呢?
为了避免产生精度差异,我们要把需要计算的数字乘以 10 的 n 次幂,换算成计算机能够精确识别的整数,然后再除以 10 的 n 次幂,大部分编程语言都是这样处理精度差异的,我们就借用过来处理一下 JS 中的浮点数精度误差。
所以如果下次再有人问你 0.1 + 0.2 等于几,或者其他类似问题,你可要小心回答咯!!比如:0.000002*100 = 0.00019999999999999998
解决方法二:网上找到了一些解决办法,就是重新写了一些浮点运算的函数。
下面就把这些方法摘录下来,以供遇到同样问题的朋友参考:
程序代码
1.除法函数
/*除法函数,用来得到精确的除法结果 *说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 *调用:accDiv(arg1,arg2) *返回值:arg1除以arg2的精确结果 */ function accDiv(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); } } //给Number类型增加一个div方法,调用起来更加方便。 Number.prototype.div = function (arg){ return accDiv(this, arg); }
2.乘法函数
/* *乘法函数,用来得到精确的乘法结果 *说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 *调用:accMul(arg1,arg2) *返回值:arg1乘以arg2的精确结果 */ function accMul(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) } //给Number类型增加一个mul方法,调用起来更加方便。 Number.prototype.mul = function (arg){ return accMul(arg, this); }
3.加法函数
/*用来得到精确的加法结果 *说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。 *调用:accAdd(arg1,arg2) *返回值:arg1加上arg2的精确结果 */ function accAdd(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 (arg1*m+arg2*m)/m } //给Number类型增加一个add方法,调用起来更加方便。 Number.prototype.add = function (arg){ return accAdd(arg,this); }
4.减法函数
//减法函数
function accSub(arg1, arg2) {
var r1, r2, m, n;
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));
//last modify by deeka
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}