`

JS中浮点数运算误差处理

 
阅读更多

先来个简单的代码片段:

> console.log(0.1 + 0.2)
> 0.30000000000000004

好奇怪的结果,怎么会是0.30000000000000004呢?难道是Javascript语言的bug还是chrome dev tools的bug?

 

其实,这不是语言的bug或者宿主环境的bug。目前所有的程序设计语言在对浮点数进行四则运算时,都会涉及到浮点数精确度的问题。

 

我们知道在计算机的世界中,计算机只认识0,1,我们传入的十进制数字并不会被计算机直接识别。计算机会先将其转换成二进制,然后使用转换后的二进制进行计算。

 

那么0.1和0.2转换成二进制分别是,

 

(0.1) => 0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 101

(0.2) => 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 01

然后对上面的两个二进制数字做加法,得到的结果是,

 

0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101 01

再把这个二进制转换成十进制,就是我们前面的结果0.30000000000000004了。

计算机世界的数值计算基本上都是这个过程,只不过C++、C#、Java这些传统强类型语言将这个边界问题封装在内部了,它们在进行浮点数四则运算时,会在语言层面自动解决这个问题。而Javascript作为一门弱类型的语言,它在语言层面并没有对这个问题进行处理,所以需要我们手动去处理。

 

那么,我们如何去避免这个问题呢?

 

基本上有两种思路。

 

先扩大数值到javascript可精确识别的精度级别(比如个位数级别)然后再进行计算,然后再对结果除以放大的倍数。

在运算的过程中通过toFixed()指定运算的精度要求。

比如我们要计算0.1 + 0.2,按照第一种思路,我们可以这么来做,

 

var a = 0.1;

var b = 0.2;

 

var ret = (a * 10 + b * 10) / 10;

console.log(ret);

这里,我们在计算之前,现将a和b乘以10(放大10倍,当然放大100、1000倍也是可以的),此时a和b已经不再是浮点数了,整数的四则运算当然是不需要考虑精度问题啦。最后我们还需要除以之前放大的倍数,得到正确的最终结果。其实这是一种迂回的办法来规避掉浮点数的四则运算精度问题。

 

按照第二种思路,我们可以这么来做,

 

var a = 0.1;

var b = 0.2;

 

var ret = (a + b).toFixed(1);

var ret = parseFloat(ret);

 

注意toFixed()返回的是一个String,所以我们还需要进行parseFloat或Number操作。

 

附上针对js浮点运算的公共方法:

var util = {

/**
* 加法优化,避免浮点误差
* @param arg1
* @param arg2
* @returns {String}
*/
calcAdd: function(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;
},

/**
* 减法优化,避免浮点误差
* @param arg1
* @param arg2
* @returns {String}
*/
calcSub: function accSub(arg1, arg2) {
var r1,r2,m,n;
try{r1=arg2.toString().split(".")[1].length}catch(e){r1=0}
try{r2=arg1.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);
},

/**
* 乘法优化,避免浮点误差
* @param arg1
* @param arg2
* @returns {String}
*/
calcMul: 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);
},

/**
* 除法优化,避免浮点误差
* @param arg1
* @param arg2
* @returns {String}
*/
calcDiv: 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){}
r1=Number(arg1.toString().replace(".",""));
r2=Number(arg2.toString().replace(".",""));
return (r1/r2)*Math.pow(10,t2-t1);
}

};


简化版:

var util = (function(){
        var getFuncByType = function(typeText){
            return new Function('a', 'b', 'var aR = 0, bR = 0;try{aR = Number(a).toString().split(".")[1].length;}catch(e){}try{bR = Number(b).toString().split(".")[1].length;}catch(e){}return Number((a'+ typeText +'b).toFixed(aR+bR));');
        };
        return {
            // 加
            calcAdd: function(a, b){
                return getFuncByType("+")(a, b);
            },
            // 减
            calcSub: function(a, b){
                return getFuncByType("-")(a, b);
            },
            // 乘
            calcMul: function(a, b){
                return getFuncByType("*")(a, b);
            },
            // 除
            calcDiv: function(a, b){
                return getFuncByType("/")(a, b);
            }
        }
    })();

 

 

分享到:
评论

相关推荐

    详解JS– 浮点数运算处理

    最近在做一个项目,页面上会存在一些JS浮点数的运算,发现JS浮点数运算存在一些bug.譬如: 0.1+0.2 == 0.30000000000000004 0.1 + 0.7 == 0.7999999999999999 7*0.8 == 5.6000000000000005 5.6/7 == 0....

    JavaScript解决浮点数计算不准确问题的方法分析

    主要介绍了JavaScript解决浮点数计算不准确问题的方法,结合实例形式分析了javascript浮点数运算精度误差的原因以及相关的解决方法与具体操作技巧,需要的朋友可以参考下

    浅析js中的浮点型运算问题

    到网上一搜,有网友说这是一个JS浮点数运算Bug,找了解决方法: 方法一:有js自定义函数 代码如下:[removed] //加法函数,用来得到精确的加法结果 //说明:javascript的加法结果会有误差,在两个浮点数相加的时候会...

    Javascript浮点数乘积运算出现多位小数的解决方法

    这是由于在运算的时候先把浮点数转化成二进制后进行运算,但是有的小数在二进制编码后出现无限循环,因而导致计算出现了误差,在其它变成语言中也有类似的问题。 原因解释参考自百度知道: 例如:求1038.1-1000 ...

    Js四则运算函数代码

    javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果

    js中浮点型运算BUG的解决方法说明

    曾经项目用到过的,之前在网上找到此代码,但在特定条件下除法和加法运算依然会出现BUG个人对此稍作优化 代码如下://除法函数,用来得到精确的除法结果//说明:javascript的除法结果会有误差,在两个浮点数相除的...

    CalcEval科学计算

    CalcEval引擎是一个专门解决javascript浮点数误差的的引擎,能够完美的解决各种复合的运算,最终输出正确的结果。

    javascript 除法、乘法、加法精确计算

    javascript的除法、乘法、加法结果会有误差,在两个浮点数相除的时候会比较明显。自定义几个函数返回较为精确的除法结果。

    JavaScript实现计算圆周率到小数点后100位的方法示例

    浮点数的有效数位是16位,我自己做了一个大数类,能存储100位有效数位,并实现了大数类的基本运算。我用它来计算圆周率(割圆法,即多边形逼近),得到了小数点后一百位有效数字,比对了Machin 公式的计算结果,没有...

Global site tag (gtag.js) - Google Analytics