js精度偏差

以前雖然有看到過 js 精度相關的文章。但也都沒有「印象深入」 ,可是今天"有幸"遇到了。javascript

作一個項目,進行頁面調試的時候,html

當數量增長到3時總價格變得好長好長java

 

立馬在控制檯驗證了一下,算出這麼多個小數。git

還好以前有看過這方面的文章,知道是js的精度問題(但也不是js自己的問題,而是二進制的問題)。編程

 

 正確的應該是 239.7瀏覽器

 

而後找了幾篇文章,最後打算用那個插件。能夠用字符串進行運算,因此功能很強呀。也不大,5k而已~什麼?你給我說流量?優化?百度首頁?淘寶?好吧,等有門作那樣的項目時再說吧。網絡

想測試更多精度問題的問題試試:編程語言

9007199254740992 + 1 // 丟失
9007199254740992 + 2 // 未丟失
9007199254740992 + 3 // 丟失
9007199254740992 + 4 // 未丟失

var x = 0.3 - 0.2; //30美分減去20美分
var y = 0.2 - 0.1; //20美分減去10美分
x == y; // =>false,兩值不相等
x == 0.1; // =>false,真實值爲:0.09999999999999998
y == 0.1; // =>true

 

使用不一樣的函數分別計算+、-、*、/ :

//問題好比:7*0.8 JavaScript算出來就是:5.6000000000000005
            //加法函數,用來獲得精確的加法結果
            //說明: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);
            }
            //如:
            var t1 = 6.60;
            var t2 = 1.32;
            var t3 = 1.2;
            var t4 = 1.2;
            var t5 = 1.2;
            alert(Number(t1).add(Number(t2)).add(Number(t3)).add(Number(t4)).add(Number(t5)));
            //減法函數,用來獲得精確的減法結果
            function Subtr(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);
            }
            //乘法函數,用來獲得精確的乘法結果
            //說明: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);
            }
            //除法函數,用來獲得精確的除法結果
            //說明: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方法,調用起來更加方便。
View Code

 

 

JS中toFixed()方法的問題及解決方案

四捨五入失效。ide

《浮點數精度問題的前世此生?爲何計算機會 丟失小數……》函數

http://www.zhoulujun.cn/zhoulujun/html/theory/computBase/2016_0714_7860.html

1.335.toFixed(2) // 1.33

重寫 toFixed ,有人說重寫後的返回值改變了。

http://www.cnblogs.com/gushen/archive/2012/11/20/2778324.html

<html>
    <head>
        <script type="text/javascript">
            Number.prototype.toFixed=function (d) { 
                var s=this+""; 
                if(!d)d=0; 
                if(s.indexOf(".")==-1)s+="."; 
                s+=new Array(d+1).join("0"); 
                if(new RegExp("^(-|\\+)?(\\d+(\\.\\d{0,"+(d+1)+"})?)\\d*$").test(s)){
                    var s="0"+RegExp.$2,pm=RegExp.$1,a=RegExp.$3.length,b=true;
                    if(a==d+2){
                        a=s.match(/\d/g); 
                        if(parseInt(a[a.length-1])>4){
                            for(var i=a.length-2;i>=0;i--){
                                a[i]=parseInt(a[i])+1;
                                if(a[i]==10){
                                    a[i]=0;
                                    b=i!=1;
                                }else break;
                            }
                        }
                        s=a.join("").replace(new RegExp("(\\d+)(\\d{"+d+"})\\d$"),"$1.$2");

                    }if(b)s=s.substr(1); 
                    return (pm+s).replace(/\.$/,"");
                }return this+"";

            };
        </script>
    </head>
    <body>
        <input type="button" value="顯示0.009.toFixed(2)" onclick="alert(0.009.toFixed(2))"><br />
        <input type="button" value="顯示0.123.toFixed(2)" onclick="alert(0.123.toFixed(2))"><br />
        <input type="button" value="顯示0.125.toFixed(2)" onclick="alert(0.125.toFixed(2))"><br />
        <input type="button" value="顯示0.126.toFixed(2)" onclick="alert(0.126.toFixed(2))"><br />
        <input type="button" value="顯示20.445.toFixed(2)" onclick="alert(20.445.toFixed(2))"><br />
        <input onclick="alert(20.405.toFixed(2))" type="button" value="顯示20.405.toFixed(2)"> <br />
        <input onclick="alert(20.415.toFixed(2))" type="button" value="顯示20.415.toFixed(2)"> <br />
        <input onclick="alert(20.425.toFixed(2))" type="button" value="顯示20.425.toFixed(2)"> <br />
        <input onclick="alert(20.435.toFixed(2))" type="button" value="顯示20.435.toFixed(2)"> <br />
        <input onclick="alert(20.445.toFixed(2))" type="button" value="顯示20.445.toFixed(2)"> <br />
        <input onclick="alert(20.455.toFixed(2))" type="button" value="顯示20.455.toFixed(2)"> <br />
        <input onclick="alert(20.465.toFixed(2))" type="button" value="顯示20.465.toFixed(2)"> <br />
        <input onclick="alert(20.475.toFixed(2))" type="button" value="顯示20.475.toFixed(2)"> <br />
        <input onclick="alert(20.485.toFixed(2))" type="button" value="顯示20.485.toFixed(2)"> <br />
        <input onclick="alert(20.495.toFixed(2))" type="button" value="顯示20.495.toFixed(2)"> <br />
        <input onclick="alert(0.05.toFixed(1))" type="button" value="顯示0.05.toFixed(1)"> <br />
        <input onclick="alert(0.15.toFixed(1))" type="button" value="顯示0.15.toFixed(1)"> <br />
        <input onclick="alert(0.25.toFixed(1))" type="button" value="顯示0.25.toFixed(1)"> <br />
        <input onclick="alert(0.35.toFixed(1))" type="button" value="顯示0.35.toFixed(1)"> <br />
        <input onclick="alert(0.45.toFixed(1))" type="button" value="顯示0.45.toFixed(1)"> <br />
        <input onclick="alert(0.55.toFixed(1))" type="button" value="顯示0.55.toFixed(1)"> <br />
        <input onclick="alert(0.65.toFixed(1))" type="button" value="顯示0.65.toFixed(1)"> <br />
        <input onclick="alert(0.75.toFixed(1))" type="button" value="顯示0.75.toFixed(1)"> <br />
        <input onclick="alert(0.85.toFixed(1))" type="button" value="顯示0.85.toFixed(1)"> <br />
        <input onclick="alert(0.95.toFixed(1))" type="button" value="顯示0.95.toFixed(1)"> <br />
    </body>
</html>
View Code

 

下面的文章均來源於網絡。 

 


 http://blog.csdn.net/forest_fire/article/details/50944339

若是我問你 0.1 + 0.2 等於幾?你可能會送我一個白眼,0.1 + 0.2 = 0.3 啊,那還用問嗎?連幼兒園的小朋友都會回答這麼小兒科的問題了。可是你知道嗎,一樣的問題放在編程語言中,或許就不是想象中那麼簡單的事兒了。
不信?咱們先來看一段 JS。

var numA = 0.1; 
var numB = 0.2; 
alert( (numA + numB) === 0.3 );

執行結果是 false。沒錯,當我第一次看到這段代碼時,我也理所固然地覺得它是 true,可是執行結果讓我大跌眼鏡,是個人打開方式不對嗎?非也非也。咱們再執行如下代碼試試就知道結果爲何是 false 了。

var numA = 0.1; 
var numB = 0.2; 
alert( numA + numB );

原來,0.1 + 0.2 = 0.30000000000000004。是否是很奇葩?其實對於浮點數的四則運算,幾乎全部的編程語言都會有相似精度偏差的問題,只不過在 C++/C#/Java 這些語言中已經封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,因此精度偏差的問題就顯得格外突出。下面就分析下爲何會有這個精度偏差,以及怎樣修復這個偏差。

首先,咱們要站在計算機的角度思考 0.1 + 0.2 這個看似小兒科的問題。咱們知道,能被計算機讀懂的是二進制,而不是十進制,因此咱們先把 0.1 和 0.2 轉換成二進制看看:

0.1 => 0.0001 1001 1001 1001…(無限循環)
0.2 => 0.0011 0011 0011 0011…(無限循環)

雙精度浮點數的小數部分最多支持 52 位,因此二者相加以後獲得這麼一串 0.0100110011001100110011001100110011001100110011001100 因浮點數小數位的限制而截斷的二進制數字,這時候,咱們再把它轉換爲十進制,就成了 0.30000000000000004。

原來如此,那怎麼解決這個問題呢?我想要的結果就是 0.1 + 0.2 === 0.3 啊!!!

有種最簡單的解決方案,就是給出明確的精度要求,在返回值的過程當中,計算機會自動四捨五入,好比:

var numA = 0.1; 
var numB = 0.2; 
alert( parseFloat((numA + numB).toFixed(2)) === 0.3 );

可是明顯這不是一勞永逸的方法,若是有一個方法能幫咱們解決這些浮點數的精度問題,那該多好。咱們來試試下面這個方法:

Math.formatFloat = function(f, digit) { 
    var m = Math.pow(10, digit); 
    return parseInt(f * m, 10) / m; 


var numA = 0.1; 
var numB = 0.2;

alert(Math.formatFloat(numA + numB, 1) === 0.3);

這個方法是什麼意思呢?爲了不產生精度差別,咱們要把須要計算的數字乘以 10 的 n 次冪,換算成計算機可以精確識別的整數,而後再除以 10 的 n 次冪,大部分編程語言都是這樣處理精度差別的,咱們就借用過來處理一下 JS 中的浮點數精度偏差。

若是下次再有人問你 0.1 + 0.2 等於幾,你可要當心回答咯!!

 


 

1.由於計算機只認識二進制,因此某些數字二進制是無限循環的,例如:0.1=> 0.0001 1001 1001 ...無限循環   ,因此產生了精度問題,c這類語言已經封裝好方法來避免,然而js並無,爲此帶來很多的麻煩,特別是須要頻繁計算的項目,出現bug還不容易發現。不扯皮,上解決方案:

1.化零爲整

先把小數乘以10的次冪,而後再運算。

0.1+0.2=>((0.1*10)+(0.2*10))/10=>0.3;

固然這只是思路,實際應用還有不少問題,好比要判斷有幾位小數位,當表達式複雜的時候可閱讀性的問題,個人思路是分別寫加減乘除四個運算方法,把四個方法放到windwo對象的原型中(不推薦)或者放到某個模塊類中;

 

2.CalcEval.js引擎

不想動腦的福利來了,CalcEval引擎專門解決js精度問題。

 

引入CalcEval.js

<script src="js/CalcEval.js"></script>

var ce=new CalcEval();//建立引擎對象

var result=ce.eval('0.1+0.2');//注意:表達式必須以字符串的形式傳入

 

 


 

 
javascript公式計算引擎-解決浮點數計算偏差-網頁計算器
 

咱們你們都知道,javascript在計算公式的時候,會出現偏差,致使咱們原本就應該正確的代碼,出現了咱們意想不到的結果。

例如:

45.6*13=592.8000000000001(結果應該是592.8);
0.7+0.1=0.7999999999999999(應該是0.8);
//還有N多,在此不一一列舉。

網上有一個比較承認的解決方法,就是本身去寫加法,減法,乘法,除法。

例如:

// 兩個浮點數求和
    function accAdd(num1,num2){
       var r1,r2,m;
       try{
           r1 = num1.toString().split('.')[1].length;
       }catch(e){
           r1 = 0;
       }
       try{
           r2=num2.toString().split(".")[1].length;
       }catch(e){
           r2=0;
       }
       m=Math.pow(10,Math.max(r1,r2));
       // return (num1*m+num2*m)/m;
       return Math.round(num1*m+num2*m)/m;
    }
    
    // 兩個浮點數相減
    function accSub(num1,num2){
       var r1,r2,m;
       try{
           r1 = num1.toString().split('.')[1].length;
       }catch(e){
           r1 = 0;
       }
       try{
           r2=num2.toString().split(".")[1].length;
       }catch(e){
           r2=0;
       }
       m=Math.pow(10,Math.max(r1,r2));
       n=(r1>=r2)?r1:r2;
       return (Math.round(num1*m-num2*m)/m).toFixed(n);
    }
    // 兩數相除
    function accDiv(num1,num2){
       var t1,t2,r1,r2;
       try{
           t1 = num1.toString().split('.')[1].length;
       }catch(e){
           t1 = 0;
       }
       try{
           t2=num2.toString().split(".")[1].length;
       }catch(e){
           t2=0;
       }
       r1=Number(num1.toString().replace(".",""));
       r2=Number(num2.toString().replace(".",""));
       return (r1/r2)*Math.pow(10,t2-t1);
    }
    
    function accMul(num1,num2){
       var m=0,s1=num1.toString(),s2=num2.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);
    }
View Code

 

可是有的時候,咱們須要計算一連串的公式,而且裏面包含了括號等等的複雜的符合運算,這個時候我們應該怎麼辦呢?

例如:計算(0.7+0.1)÷(45.6*13)

這樣的公式,咱們是沒法經過上面的自定義函數來解決的。所以今天給你們介紹一個比較好的計算引擎。

CalcEval.js

CalcEval引擎是一個專門解決javascript浮點數偏差的的引擎,可以完美的解決各類複合的運算,最終輸出正確的結果。

使用方法:

第一步:引入CalcEval.js

<script type="text/javascript" src="CalcEval.js"></script>

第二部:在頁面上調用CalcEval的解析引擎入口

var ce = new CalcEval();//建立引擎對象
var result = ce.eval("(0.7+0.1)/(45.6*13)");//調用引擎接口來解析公式的字符串,這個地方,必需要將公式以字符串的形式傳入。
alert(result);//查看返回結果。

就這麼簡單的過程,就能夠解決了每一個瀏覽器中的浮點數計算bug。同時也能夠製做本身的網頁計算器了。

相關文章
相關標籤/搜索