1. 程式人生 > >JS浮點計算問題

JS浮點計算問題

本文轉自:https://www.cnblogs.com/Stephenchao/p/5743805.html

用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));
}