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