JS浮點計算問題


問題

用js進行浮點數計算,結果可能會“超出預期”,大部分計算結果還是對的,但是我們可不想在計算這么嚴謹的事情上還有意外的驚喜。比如:

  • 0.3 + 0.6 = 0.8999999999999999
  • 0.3 - 0.2 = 0.09999999999999998
  • 0.3 * 1.5 = 0.44999999999999996
  • 0.3 / 0.1 = 2.9999999999999996

看完這幾個計算結果,如果你沒用過js,你可能會有點崩潰。我只能說,這就是js的魅力所在。

分析

在這之前,你需要知道以下幾點:

  • js中數字類型只有Number;
  • js的Number是IEEE 754標准的64-bits的雙精度數值

網上有很多關於此問題的解釋,由於計算機是用二進制來存儲和處理數字,不能精確表示浮點數,而js中沒有相應的封裝類來處理浮點數運算,直接計算會導致運算精度丟失。其實高級語言(c#,java)也存在此問題,只不過它們自己內部做了處理,把這種精度差異給屏蔽掉了。有些小數轉換為二進制位數是無窮的(有循環),但是64位中小數最多只有52位,因此對於位數超過的相當於被截取了,導致了精度的丟失。這個地址可以用來浮點數和IEEE 754標准的64-bits的互轉(背后是二進制的轉換),用這個我們來驗證下0.3-0.2。

  • 0.3轉換后為0.299999999999999988897769753748
  • 0.2轉換后為0.200000000000000011102230246252
  • 0.299999999999999988897769753748-0.200000000000000011102230246252=0.099999999999999977795539507496

這和js直接計算的結果0.09999999999999998想吻合。

分析下來,終於明白並不是js自身發育不良,只是沒有及時補充營養,我們只能另想出路了。

解決方法

網上已經存在很多解決方法了,我這里也沒有特別的方法,但是網上有很多方法只搞定了一半,仍然存在bug。大部分解決方法的思路是將浮點數計算轉換為整數計算,整數計算當然是沒有bug的啦。前面說網上部分方法只搞對了一半,對的一半是乘除法,加減法仍然有問題,因為加減法還存在浮點數的直接運算。

附:沒有bug的代碼。

function add(a, b) {
    var c, d, e;
    try {
        c = a.toString().split(".")[1].length;
    } catch (f) {
        c = 0;
    }
    try {
        d = b.toString().split(".")[1].length;
    } catch (f) {
        d = 0;
    }
    return e = Math.pow(10, Math.max(c, d)), (mul(a, e) + mul(b, e)) / e;
}
function sub(a, b) {
    var c, d, e;
    try {
        c = a.toString().split(".")[1].length;
    } catch (f) {
        c = 0;
    }
    try {
        d = b.toString().split(".")[1].length;
    } catch (f) {
        d = 0;
    }
    return e = Math.pow(10, Math.max(c, d)), (mul(a, e) - mul(b, e)) / e;
}
function mul(a, b) {
    var c = 0,
        d = a.toString(),
        e = b.toString();
    try {
        c += d.split(".")[1].length;
    } catch (f) {}
    try {
        c += e.split(".")[1].length;
    } catch (f) {}
    return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
}
function div(a, b) {
    var c, d, e = 0,
        f = 0;
    try {
        e = a.toString().split(".")[1].length;
    } catch (g) {}
    try {
        f = b.toString().split(".")[1].length;
    } catch (g) {}
    return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), mul(c / d, Math.pow(10, f - e));
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM