js計算精度不準確問題,想必大家都遇到過。
我們下面來探討一下。
簡單說明一下
問題原因是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);
}
希望上述內容能讓大家看明白, 幫助到正在看文字的朋友。同時也希望看完文字的朋友點個大大的贊。如有錯誤的地方,還望留言,我及時更正內容。