麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > JavaScript > 正文

JS浮點數運算結果不精確的Bug解決

2019-11-19 11:05:30
字體:
來源:轉載
供稿:網友

前言

最近在做項目的時候,涉及到產品價格的計算,經常會出現JS浮點數精度問題,這個問題,對于財務管理系統的開發者來說,是個非常嚴重的問題(涉及到錢相關的問題都是嚴重的問題),這里把相關的原因和問題的解決方案整理一下,也希望給各位提供一些參考。

一. 常見例子  

 // 加法 0.1 + 0.2 = 0.30000000000000004 0.1 + 0.7 = 0.7999999999999999 0.2 + 0.4 = 0.6000000000000001 // 減法 0.3 - 0.2 = 0.09999999999999998 1.5 - 1.2 = 0.30000000000000004 // 乘法 0.8 * 3 = 2.4000000000000004 19.9 * 100 = 1989.9999999999998 // 除法 0.3 / 0.1 = 2.9999999999999996 0.69 / 10 = 0.06899999999999999 // 比較 0.1 + 0.2 === 0.3 // false (0.3 - 0.2) === (0.2 - 0.1) // false

二. 導致原因

JavaScript 內部只有一種數字類型Number,也就是說,JavaScript 語言的底層根本沒有整數,所有數字都是以IEEE-754標準格式64位浮點數形式儲存,1與1.0是相同的。因為有些小數以二進制表示位數是無窮的。JavaScript會把超出53位之后的二進制舍棄,所以涉及小數的比較和運算要特別小心。

三. IEEE二進制浮點數算術標準(IEEE 754)

IEEE二進制浮點數算術標準(IEEE 754)是20世紀80年代以來最廣泛使用的浮點數運算標準,為許多CPU與浮點運算器所采用。這個標準定義了表示浮點數的格式(包括負零-0)與反常值(denormal number)),一些特殊數值(無窮(Inf)與非數值(NaN)),以及這些數值的“浮點數運算符”;它也指明了四種數值舍入規則和五種例外狀況(包括例外發生的時機與處理方式)。

四. 浮點數的存儲

JS的浮點數實現也是遵循IEEE 754標準,采用雙精度存儲(double precision),使用64位固定長度來表示,其中1位用來表示符號位,11位用來表示指數,52位表示尾數。如下圖:

  • ​符號位(sign):第1位是正負數符號位,0代表正數,1代表負數
  • 指數位(Exponent):中間11位存儲指數,用來表示次方數
  • 尾數位(mantissa):最后的52位是尾數,超出部分自動進一舍零

五. 浮點數的計算步驟(0.1+0.2)

【1】首先,十進制的0.1和0.2會轉換成二進制的,但是由于浮點數用二進制表示是無窮的

0.1――>0.0001 1001 1001 1001 ...(1001循環)
 0.2――>0.0011 0011 0011 0011 ...(0011循環)

【2】IEEE754標準的64位雙精度浮點數的小數部分最多支持53位二進制,多余的二進制數字被截斷,所以兩者相加之后的二進制之和是

0.0100110011001100110011001100110011001100110011001101

【3】將截斷之后的二進制數字再轉換為十進制,就成了0.30000000000000004,所以在計算時產生了誤差

六. 解決辦法

【1】引用類庫

【2】思路一:在知道小數位個數的前提下,可以考慮通過將浮點數放大倍數到整型(最后再除以相應倍數),再進行運算操作,這樣就能得到正確的結果了  

0.1 + 0.2 ――> (0.1 * 10 + 0.2 * 10) / 10 // 0.3
0.8 * 3 ――> ( 0.8 * 100 * 3) / 100         //2.4

【3】自定義一個轉換和處理函數 

 // f代表需要計算的表達式,digit代表小數位數 Math.formatFloat = function (f, digit) {  // Math.pow(指數,冪指數)  var m = Math.pow(10, digit);  // Math.round() 四舍五入  return Math.round(f * m, 10) / m; } console.log(Math.formatFloat(0.3 * 8, 1)); // 2.4 console.log(Math.formatFloat(0.35 * 8, 2)); // 2.8

【4】加法函數  

 /**  ** 加法函數,用來得到精確的加法結果  ** 說明:javascript的加法結果會有誤差,在兩個浮點數相加的時候會比較明顯。這個函數返回較為精確的加法結果。  ** 調用:accAdd(arg1,arg2)  ** 返回值:arg1加上arg2的精確結果  **/ function accAdd(arg1, arg2) {  var r1, r2, m, c;  try {  r1 = arg1.toString().split(".")[1].length;  } catch (e) {  r1 = 0;  }  try {  r2 = arg2.toString().split(".")[1].length;  } catch (e) {  r2 = 0;  }  c = Math.abs(r1 - r2);  m = Math.pow(10, Math.max(r1, r2));  if (c > 0) {  var cm = Math.pow(10, c);  if (r1 > r2) {   arg1 = Number(arg1.toString().replace(".", ""));   arg2 = Number(arg2.toString().replace(".", "")) * cm;  } else {   arg1 = Number(arg1.toString().replace(".", "")) * cm;   arg2 = Number(arg2.toString().replace(".", ""));  }  } else {  arg1 = Number(arg1.toString().replace(".", ""));  arg2 = Number(arg2.toString().replace(".", ""));  }  return (arg1 + arg2) / m; } //給Number類型增加一個add方法,調用起來更加方便。 Number.prototype.add = function (arg) {  return accAdd(arg, this); };

【5】減法函數

 /**  ** 減法函數,用來得到精確的減法結果  ** 說明: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); } // 給Number類型增加一個mul方法,調用起來更加方便。 Number.prototype.sub = function (arg) {  return accMul(arg, this); };

【6】乘法函數

 /**  ** 乘法函數,用來得到精確的乘法結果  ** 說明: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); };

【7】除法函數

  /**    ** 除法函數,用來得到精確的除法結果   ** 說明: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);  };

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 日本高清一级片 | 免费国产wwwwwww网站 | 红桃一区| 国产一区二区三区四区精 | chinese军人gay呻吟 | 成人福利在线看 | 欧美性猛交一区二区三区精品 | 免费99热在线观看 | 精品亚洲一区二区三区 | 久久久www成人免费精品 | 日韩视频在线视频 | 九九色网站 | 黄色片网站免费 | 国产午夜电影在线观看 | 久久久成人免费视频 | 福利一区二区三区视频在线观看 | 亚洲性生活视频 | 狠狠久久伊人中文字幕 | 蜜桃视频观看麻豆 | 亚州综合网 | 激情欧美在线 | 欧美一级高潮 | 免费久久精品 | 免费看欧美一级特黄a毛片 九色com | 手机免费看一级片 | 另类亚洲孕妇分娩网址 | 91精品国产日韩91久久久久久360 | 久久免费观看一级毛片 | 国产一区成人 | 男女羞羞在线观看 | 欧美性生活视频免费 | 免费观看一级 | 久久国产精品99国产 | 亚洲综合一区在线观看 | 国产欧美一区二区三区免费看 | 久色免费视频 | 亚洲精品aa | 久久国产精品久久精品国产演员表 | 亚洲爱爱网站 | 欧美在线观看视频一区 | 91美女啪啪|