js浮點數計算精度不準確問題的解決辦法

js計算精度不準確問題,想必大家都遇到過。
我們下面來探討一下。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-G8mWFmsq-1586175629785)(https://imgblog.csdnimg.cn/20200406202017733.png)]

簡單說明一下

問題原因是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) * pow(10, t2 - t1);
    }

希望上述內容能讓大家看明白, 幫助到正在看文字的朋友。同時也希望看完文字的朋友點個大大的贊。如有錯誤的地方,還望留言,我及時更正內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章