javascript
JS的浮点数计算精度丢失问题解决方案
前言:
近期在做項目的時候,遇到了一些JS浮點數精度的問題。這個問題,其實說大不大,說小不小。但是這次因為涉及到一些財務和結算的問題,然后突然發(fā)現這個小問題處理起來還是挺麻煩的。這里把相關的原因的問題的解決方案整理一下,也希望給各位提供一些參考。
案例分析:
近期的項目,由于在H5頁面上需要進行動態(tài)的金額計算,且金額涉及到了小數,因而隨之產生了JS浮點數計算的精度丟失問題。
剛開始的時候,測試給提了一個金額計算誤差的問題,剛開始我還沒怎么重視,然后瞅了瞅代碼,隨便改了改做了些異常處理,然后就給提交了。
接著,測試又提了一個bug,“6.8-0.9=5.8”。然后頓時我就蒙逼了,隨后突然意識到,JS作為解釋性語言,直接計算會有浮點數精度丟失問題。接下來,在網上找了一些資料,然后也根據具體的原理自己做了一些修改,最終解決了問題。下面就把整個問題解決的思路整理一下。
浮點數的二進制表示:
IEEE 754 標準是IEEE二進位浮點數算術標準(IEEE Standard for Floating-Point Arithmetic)的標準編號,等同于國際標準ISO/IEC/IEEE 60559。該標準由美國電氣電子工程師學會(IEEE)計算機學會旗下的微處理器標準委員會(Microprocessor Standards Committee, MSC)發(fā)布。這個標準定義了表示浮點數的格式(包括負零-0)與反常值(denormal number),一些特殊數值(無窮(Inf)與非數值(NaN)),以及這些數值的「浮點數運算子」;它也指明了四種數值修約規(guī)則和五種例外狀況(包括例外發(fā)生的時機與處理方式)。
JS的浮點數實現也是遵循IEEE 754標準,采用雙精度存儲(double precision),進行了相關的實現。其中1位用來表示符號位,11位用來表示指數,52位表示尾數。如下圖:
由于無論是采用了哪種表達方式進行怎樣的計算,到了計算機的最底層,都是通過1和0的機器碼來對具體的數據和操作進行具體的實現。由于底層實現機制的原因,浮點數在轉換為二進制表示的時候,無法精確表示這種包含小數點的數據,其本質是將浮點數轉換成了用二進制表示的最接近的近似值。下面一個例子可以用來簡單說明浮點數在轉換為二進制時候的計算方法。如下圖:
0.02625=0.000001101(二進制),無法精確求出二進制表示,因此采用“四舍五入法”(逢1進,逢0舍)。
以上就是問題產生的原理,很多編譯型語言如Java,c#等都對浮點數的處理進行了封裝。因此平時大多數情況下,并不會出現明顯的可見的問題。js本身作為解釋性語言,好像這點上有著天生的劣勢。隨后本人在網上找了很多解決方式,也大多數是通過自定義的方法去解決這個問題,原理基本都大同小異。
解決方案:
本質上在處理這類問題的時候,基本的思路就是通過將浮點數轉換成整數進行計算,然后再將整數的小數點位調整,轉回正確的浮點數結果。
原生計算
console.log(6.8-0.9); console.log(6.8-0.8); console.log(6.8-0.4); console.log(6.8-0.3); //結果 5.8999999999999995 6 6.3999999999999995 6.5第一步,定義一個自定義的轉換和處理函數:
Math.formatFloat = function (f, digit) {var m = Math.pow(10, digit);return parseInt(f * m, 10) / m; }此時調用這個自定義的函數,來處理上面的原生計算:
console.log(Math.formatFloat(6.8-0.9,1)); console.log(Math.formatFloat(6.8-0.8,1)); console.log(Math.formatFloat(6.8-0.4,1)); console.log(Math.formatFloat(6.8-0.3,1)); //此時結果 5.8 6 6.3 6.5仔細看輸出的結果,會發(fā)現,6.8-0.9應該輸出結果5.9,6.8-0.4應該輸出6.4,這里轉換結果還是不正確。可以將自定義方法內部的結果進行打印,進行對比。
console.log(6.8-0.9); console.log((6.8-0.9)*10);// console.log(parseInt((6.8-0.9)*10,10)); //在轉換結果的時候出現了問題,轉換為整數時,小數點后直接被截斷了 console.log(((6.8-0.9)*10)/10); //結果 5.8999999999999995 58.99999999999999 58 5.8999999999999995這里自定義的函數,做一下具體的處理。在浮點數計算的時候,很多時候產生的都是這種極限數據,如果要精確進行整數轉換,要放大的倍數過大。這里我們可以用四舍五入對轉換的過程進行優(yōu)化:
Math.ceil((6.8-0.9)*10);//向上取整 59 Math.floor((6.8-0.9)*10);//想下取整 58 Math.round((6.8-0.9)*10);//四舍五入 59優(yōu)化之后的自定義函數:
Math.formatFloat = function (f, digit) {var m = Math.pow(10, digit);return Math.round(f * m, 10) / m; }此時重新調用函數對計算結果進行打印:
console.log(Math.formatFloat(6.8-0.9,2)); console.log(Math.formatFloat(6.8-0.8,2)); console.log(Math.formatFloat(6.8-0.4,2)); console.log(Math.formatFloat(6.8-0.3,2)); //打印結果 5.9 6 6.4 6.5寫在最后:
近些日子,發(fā)現js的火爆程度實在有點出乎意料。而且隨著node等技術的出現,讓js這樣的原來以慢著稱的語言有了和服務端開發(fā)語言的能力。之前微軟,蘋果,oracle等公司都嘗試用自己的語言和生態(tài)體系,統治整個開發(fā)者的世界。然而,我覺得說不定js能完成,這些巨頭沒有完成的工作。也許開發(fā)語言的大一統,會在不遠的未來實現。
希望我的文字能給你帶來幫助:
碼字不易,與君共勉!
總結
以上是生活随笔為你收集整理的JS的浮点数计算精度丢失问题解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jetson nano 连接wifi
- 下一篇: 用SpringBoot 做代web理服务