淺談JavaScript浮點數及其運算

    JavaScript 只有一種數字類型 Number,並且在Javascript中全部的數字都是以IEEE-754標準格式表示的。浮點數的精度問題不是JavaScript特有的,由於有些小數以 二進制表示位數是無窮的

    十進制           二進制
    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

相關文章
相關標籤/搜索