浮点数运算的精度问题
問題描述
在 JavaScript 中整數和浮點數都屬于 Number 數據類型,所有數字都是以 64 位浮點數形式儲存,即便整數也是如此。 所以我們在打印 1.00 這樣的浮點數的結果是 1 而非 1.00 。在一些特殊的數值表示中,例如金額,這樣看上去有點變扭,但是至少值是正確了。然而要命的是,當浮點數做數學運算的時候,你經常會發現一些問題,舉幾個例子:
問題的原因
似乎是不可思議。小學生都會算的題目,JavaScript 不會?我們來看看其真正的原因。
JavaScript 里的數字是采用 IEEE 754 標準的 64 位雙精度浮點數。該規范定義了浮點數的格式,對于64位的浮點數在內存中的表示,最高的1位是符號位,接著的11位是指數,剩下的52位為有效數字,具體:
第0位:符號位, s 表示 ,0表示正數,1表示負數;
第1位到第11位:儲存指數部分, e 表示 ;
第12位到第63位:儲存小數部分(即有效數字),f 表示,
如圖:
計算過程
比如在 JavaScript 中計算 0.1 + 0.2時,到底發生了什么呢?
首先,十進制的0.1和0.2都會被轉換成二進制,但由于浮點數用二進制表達時是無窮的,例如。
IEEE 754 標準的 64 位雙精度浮點數的小數部分最多支持 53 位二進制位,所以兩者相加之后得到二進制為
0.0100110011001100110011001100110011001100110011001100因浮點數小數位的限制而截斷的二進制數字,再轉換為十進制,就成了 0.30000000000000004。所以在進行算術計算時會產生誤差。
整數的精度問題
在 Javascript 中,整數精度同樣存在問題,先來看看問題:
同樣的原因,在 JavaScript 中 Number類型統一按浮點數處理,整數是按最大54位來算最大(253 - 1,Number.MAX_SAFE_INTEGER,9007199254740991) 和最小(-(253 - 1),Number.MIN_SAFE_INTEGER,-9007199254740991) 安全整數范圍的。所以只要超過這個范圍,就會存在被舍去的精度問題。
當然這個問題并不只是在 Javascript 中才會出現,幾乎所有的編程語言都采用了 IEEE-745 浮點數表示法,任何使用二進制浮點數的編程語言都會有這個問題,只不過在很多其他語言中已經封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,所以精度誤差的問題就顯得格外突出。
解決方案
加法函數
減法函數
/**** 減法函數,用來得到精確的減法結果** 說明: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); };乘法函數
/**** 乘法函數,用來得到精確的乘法結果** 說明: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); };除法函數
/** ** 除法函數,用來得到精確的除法結果** 說明: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); };總結
以上是生活随笔為你收集整理的浮点数运算的精度问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FAT32和NTFS最大支持的单个文件大
- 下一篇: JS正则匹配所有中文字符