JS中 0.1+0.2 = 0.3000000000000004的問題,在不少業務場景裏都是一個使人頭痛的問題。尤爲是在大型的電商企業,貨幣基金股票行業的網頁中,JS四則運算和toFixed精度問題更是讓人防不勝防。 京東曾經發生過一塊兒線上toFixed精度問題,差點釀成大錯: html
在剖析JS精度問題以前,要簡單描述下JS計算的方式背景git
一、在JS內部全部的計算都是以二進制方式計算的。github
二、JS內部沒法無限制保存二進制數值的長度。(最長52位)算法
這兩點其實都不難理解。計算機底層都是0和1,固然,計算機也不能保留無限長(無限大)的東西。chrome
知道了這兩點,那麼天然也就不難理解爲何JS在計算超大的數值的時候,會出現問題了,就好像你永遠沒法算清宇宙裏有多少顆星星同樣,計算機也不行。npm
那麼爲何計算很小浮點數的時候也會出問題呢,答案就是,在JS內部,浮點數也是用很長很長的二進制表示的(具體爲何請參考計算機原理)。瀏覽器
在JS的世界裏,0.1轉換爲二進制是0.00011001100110011…你會發現這串數字是無數個0.11在循環..(0.0(0011)(0011)(0011)(0011)…還有無數0011)沒辦法,在JS裏,浮點數就是轉化爲無窮長的二進制,因此JS只能截取這串二進制的前52位進行二進制的相加,那麼下面不用再說了,天然就會出現精度問題。網絡
這也能夠解釋,爲何JS中整型的數值加減不會出現問題,可是超大數值和浮點數計算會出現問題。oracle
toFixed問題其實很簡單,就是瀏覽器的坑。 es5
能夠看到,在IE和chrome兩個瀏覽器下面,一個簡單的四捨五入,顯示的結果徹底不一樣。就是由於不一樣的瀏覽器廠商對於四捨五入沒有統一的標準,就讓咱們這些開發人員踩坑。。 在IE裏,toFixed就是採用常規的四捨五入方法,然鵝,在chrome倒是採用金融界的更爲精確的四捨六入五成雙方法,雖然這個方法更爲科學,更爲精確,可是卻畢竟大多數人不是金融學家也不是科學家,仍是四捨五入更容易被常人所接受。。。
更正:
在toFixed精度問題上直接參考了大部分網絡解釋,欠缺實踐,沒有深刻考究,上面四捨六入五成雙的方法經反覆驗證實際上是錯誤的(上圖就打臉了。。),再次查閱一些資料後,通過官方查證toFixed 原生API以下:
這個方法通過10幾回toFixed驗證後發現都是和結果相吻合的,應該是正確的問題根源所在。
(PS:該錯誤對解決方案無影響)
前面提到JS中整型運算是沒有問題的,那麼把浮點數擴大相應的整數倍,轉化成整型在進行計算,計算完縮小爲整數倍不就能夠解決這個問題了麼。 這個方案確實是可行的,然鵝,怎麼實施這個方案,卻須要琢磨下。 首先第一想到的是乘法,512.06*100就能夠轉化爲 51206了,然鵝。。
解決方案的思路就是這樣,具體的代碼寫起來很相對比較複雜,我參考網上的function本身整合了一個npm包,須要的朋友能夠本身查看下源碼。 整體的思路就是上文介紹的,具體代碼實現能夠參考源碼(若是能順便點個贊,那最好啦)
toFixed的方案相對要好解決,只要有了精度沒問題的四則運算,四捨五入只要直接判斷下浮點數須要精確位數是否大於5便可,具體的代碼實現也能夠參考源碼。
解決方案核心的點就在於用字符串方式來解決小數點精度問題,雖然實現上有點複雜,須要考慮的狀況比較多,可是確實是最穩妥的作法。若是有須要高精度業務場景的小夥伴能夠直接install我封裝的npm包,就能夠直接用啦,教程參考github哦!
最後,若是這篇文章對你有點點幫助的話,歡迎動動鼠標點個贊哦!
(純屬我的手動打字,有手誤或者技術錯誤的地方,確定多多指正!)