在編寫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)
}
那麼如何解決此類問題呢?問題根源在於:
浮點數值的最高進度是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)
}
究其原理,是把要做運算的兩個操作數轉換爲整數去計算避免精度問題,在計算後(根據轉換爲整數時的放大倍數)對計算結果運算還原,得出準確結果。
這樣當我們想讓程序在盤口價格加一跳最小价格精度的時候掛單,就不用擔心數值精度問題了
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])
}
有興趣的同學,可以讀一下代碼,瞭解計算過程,歡迎提出問題,一起學習一起進步。