天天看點

JavaScript政策設計時數值計算精度問題解決方案

在編寫JavaScript政策時,由于腳本語言自身的一些問題,經常導緻計算時數值精确度問題。對于程式中一些計算、邏輯判斷有一定影響。例如,在計算

1 - 0.8

或者

0.33 * 1.1

時,會計算出有誤差的資料:

function main() {
    var a = 1 - 0.8
    Log(a)
   
    var c = 0.33 * 1.1
    Log(c) 
}
           
JavaScript政策設計時數值計算精度問題解決方案

那麼如何解決此類問題呢?問題根源在于:

浮點數值的最高進度是17位小數,但在進行運算的時候其精确度卻遠遠不如整數;整數在進行運算的時候都會轉成10進制; 而Java和JavaScript中計算小數運算時,都會先将十進制的小數換算到對應的二進制,一部分小數并不能完整的換算為二進制,這裡就出現了第一次的誤差。待小數都換算為二進制後,再進行二進制間的運算,得到二進制結果。然後再将二進制結果換算為十進制,這裡通常會出現第二次的誤差。

為了解決這個問題,在網上搜尋了一些解決方案,經過測試使用,如下方案比較好的解決了這個問題:

function mathService() {
    // 加法
    this.add = function(a, b) {
        var c, d, e;
        try {
            c = a.toString().split(".")[1].length;   // 擷取a的小數位長度
        } catch (f) {
            c = 0;
        }
        try {
            d = b.toString().split(".")[1].length;   // 擷取b的小數位長度
        } catch (f) {
            d = 0;
        }
        //先求e,把a、b 同時乘以e轉換成整數相加,再除以e還原
        return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) + this.mul(b, e)) / e;
    }
    
    // 乘法
    this.mul = function(a, b) {       
        var c = 0,
            d = a.toString(),         // 轉換為字元串 
            e = b.toString();         // ...
        try {
            c += d.split(".")[1].length;      // c 累加a的小數位長度
        } catch (f) {}
        try {
            c += e.split(".")[1].length;      // c 累加b的小數位長度
        } catch (f) {}
        // 轉換為整數相乘,再除以10^c ,移動小數點,還原,利用整數相乘不會丢失精度
        return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
    }

    // 減法
    this.sub = function(a, b) {
        var c, d, e;
        try {
            c = a.toString().split(".")[1].length;  // 擷取a的小數位長度
        } catch (f) {
            c = 0;
        }
        try {
            d = b.toString().split(".")[1].length;  // 擷取b的小數位長度
        } catch (f) {
            d = 0;
        }
        // 和加法同理
        return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) - this.mul(b, e)) / e;
    }

    // 除法
    this.div = function(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(".", "")), this.mul(c / d, Math.pow(10, f - e));
    }
}

function main() {
    var obj = new mathService()
    
    var a = 1 - 0.8
    Log(a)
    
    var b = obj.sub(1, 0.8)
    Log(b)
    
    var c = 0.33 * 1.1
    Log(c)
    
    var d = obj.mul(0.33, 1.1)
    Log(d)
}
           
JavaScript政策設計時數值計算精度問題解決方案

究其原理,是把要做運算的兩個操作數轉換為整數去計算避免精度問題,在計算後(根據轉換為整數時的放大倍數)對計算結果運算還原,得出準确結果。

這樣當我們想讓程式在盤口價格加一跳最小價格精度的時候挂單,就不用擔心數值精度問題了

function mathService() {
....    // 省略
}

function main() {
    var obj = new mathService()
    var depth = exchange.GetDepth()
    exchange.Sell(obj.add(depth.Bids[0].Price, 0.0001), depth.Bids[0].Amount, "買一訂單:", depth.Bids[0]) 
}
           

有興趣的同學,可以讀一下代碼,了解計算過程,歡迎提出問題,一起學習一起進步。

繼續閱讀