十進制 二進制
0.1 0.0001 1001 1001 1001 ...
0.2 0.0011 0011 0011 0011 ...
0.3 0.0100 1100 1100 1100 ...
0.4 0.0110 0110 0110 0110 ...
0.5 0.1
0.6 0.1001 1001 1001 1001 ...javascript
因此好比 1.1,其程序實際上沒法真正的表示 ‘1.1',而只能作到必定程度上的準確,這是沒法避免的精度丟失:1.09999999999999999java
在JavaScript中問題還要複雜些,這裏只給一些在Chrome中測試數據:git
console.log(1.0-0.9 == 0.1) //false console.log(1.0-0.8 == 0.2) //false console.log(1.0-0.7 == 0.3) //false console.log(1.0-0.6 == 0.4) //true console.log(1.0-0.5 == 0.5) //true console.log(1.0-0.4 == 0.6) //true console.log(1.0-0.3 == 0.7) //true console.log(1.0-0.2 == 0.8) //true console.log(1.0-0.1 == 0.9) //true
那如何來避免這類 1.0-0.9 != 0.1 的非bug型問題發生呢?下面給出一種目前用的比較多的解決方案, 在判斷浮點運算結果前對計算結果進行精度縮小,由於在精度縮小的過程總會自動四捨五入: 函數
(1.0-0.9).toFixed(digits) // toFixed() 精度參數digits須在0與20之間 console.log(parseFloat((1.0-0.9).toFixed(10)) === 0.1) //true console.log(parseFloat((1.0-0.8).toFixed(10)) === 0.2) //true console.log(parseFloat((1.0-0.7).toFixed(10)) === 0.3) //true console.log(parseFloat((11.0-11.8).toFixed(10)) === -0.8) //true
寫成一個方法:工具
//經過isEqual工具方法判斷數值是否相等 function isEqual(number1, number2, digits){ digits = digits == undefined? 10: digits; // 默認精度爲10 return number1.toFixed(digits) === number2.toFixed(digits); } console.log(isEqual(1.0-0.7, 0.3)); //true //原型擴展方式,更喜歡面向對象的風格 Number.prototype.isEqual = function(number, digits){ digits = digits == undefined? 10: digits; // 默認精度爲10 return this.toFixed(digits) === number.toFixed(digits); } console.log((1.0-0.7).isEqual(0.3)); //true
接下來,再來試試浮點數的運算,測試
console.log(1.79+0.12) //1.9100000000000001 console.log(2.01-0.12) //1.8899999999999997 console.log(1.01*1.3) //1.3130000000000002 console.log(0.69/10) //0.06899999999999999
解決方案:this
//加法函數,用來獲得精確的加法結果 //說明:javascript的加法結果會有偏差,在兩個浮點數相加的時候會比較明顯。這個函數返回較爲精確的加法結果。 //調用:accAdd(arg1,arg2) //返回值:arg1加上arg2的精確結果 function accAdd(arg1,arg2){ var r1,r2,m; try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0} try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0} m=Math.pow(10,Math.max(r1,r2)) return (arg1*m+arg2*m)/m } //給Number類型增長一個add方法,調用起來更加方便。 Number.prototype.add = function (arg){ return accAdd(arg,this); } //減法函數,用來獲得精確的減法結果 //說明:javascript的加法結果會有偏差,在兩個浮點數相加的時候會比較明顯。這個函數返回較爲精確的減法結果。 //調用:accSub(arg1,arg2) //返回值:arg1減去arg2的精確結果 function accSub(arg1,arg2){ var r1,r2,m,n; try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0} try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0} m=Math.pow(10,Math.max(r1,r2)); //last modify by deeka //動態控制精度長度 n=(r1>=r2)?r1:r2; return ((arg1*m-arg2*m)/m).toFixed(n); } //除法函數,用來獲得精確的除法結果 //說明:javascript的除法結果會有偏差,在兩個浮點數相除的時候會比較明顯。這個函數返回較爲精確的除法結果。 //調用:accDiv(arg1,arg2) //返回值:arg1除以arg2的精確結果 function accDiv(arg1,arg2){ var t1=0,t2=0,r1,r2; try{t1=arg1.toString().split(".")[1].length}catch(e){} try{t2=arg2.toString().split(".")[1].length}catch(e){} with(Math){ r1=Number(arg1.toString().replace(".","")) r2=Number(arg2.toString().replace(".","")) return (r1/r2)*pow(10,t2-t1); } } //給Number類型增長一個div方法,調用起來更加方便。 Number.prototype.div = function (arg){ return accDiv(this, arg); } //乘法函數,用來獲得精確的乘法結果 //說明:javascript的乘法結果會有偏差,在兩個浮點數相乘的時候會比較明顯。這個函數返回較爲精確的乘法結果。 //調用:accMul(arg1,arg2) //返回值:arg1乘以arg2的精確結果 function accMul(arg1,arg2) { var m=0,s1=arg1.toString(),s2=arg2.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) } //給Number類型增長一個mul方法,調用起來更加方便。 Number.prototype.mul = function (arg){ return accMul(arg, this); }
//驗證一下: console.log(accAdd(1.79, 0.12)); //1.91 console.log(accSub(2.01, 0.12)); //1.89 console.log(accDiv(0.69, 10)); //0.069
console.log(accMul(1.01, 1.3)); //1.313
改造以後,能夠愉快地進行浮點數加減乘除操做了~ spa