一、Javascript精度問題業務背景
JS中 0.1+0.2 = 0.3000000000000004的問題,在很多業務場景裡都是一個令人頭痛的問題。尤其是在大型的電商企業,貨幣基金股票行業的網頁中,JS四則運算和toFixed精度問題更是讓人防不勝防。 京東曾經發生過一起線上toFixed精度問題,差點釀成大錯:

二、剖析Javascript精度問題原因
1、超大數和浮點數四則運算精度問題原因
在剖析JS精度問題之前,要簡單描述下JS計算的方式背景
1、在JS內部所有的計算都是以二進位制方式計算的。
2、JS內部無法無限制儲存二進位制數值的長度。(最長52位)
這兩點其實都不難理解。計算機底層都是0和1,當然,計算機也不能保留無限長(無限大)的東西。
知道了這兩點,那麼自然也就不難理解為什麼JS在計算超大的數值的時候,會出現問題了,就好像你永遠無法算清宇宙裡有多少顆星星一樣,計算機也不行。
那麼為什麼計算很小浮點數的時候也會出問題呢,答案就是,在JS內部,浮點數也是用很長很長的二進位制表示的(具體為什麼請參考計算機原理)。
在JS的世界裡,0.1轉換為二進位制是0.00011001100110011…你會發現這串數字是無數個0.11在迴圈..(0.0(0011)(0011)(0011)(0011)…還有無數0011)沒辦法,在JS裡,浮點數就是轉化為無窮長的二進位制,所以JS只能擷取這串二進位制的前52位進行二進位制的相加,那麼下面不用再說了,自然就會出現精度問題。
這也可以解釋,為什麼JS中整型的數值加減不會出現問題,但是超大數值和浮點數計算會出現問題。
2、toFixed()精度問題
toFixed問題其實很簡單,就是瀏覽器的坑。


可以看到,在IE和chrome兩個瀏覽器下面,一個簡單的四捨五入,顯示的結果完全不同。就是因為不同的瀏覽器廠商對於四捨五入沒有統一的標準,就讓我們這些開發人員踩坑。。
在IE裡,toFixed就是採用常規的四捨五入方法,然鵝,在chrome卻是採用金融界的更為精確的四捨六入五成雙方法,雖然這個方法更為科學,更為精確,但是卻畢竟大多數人不是金融學家也不是科學家,還是四捨五入更容易被常人所接受。。。
更正:
在toFixed精度問題上直接參考了大部分網路解釋,欠缺實踐,沒有深入考究,上面四捨六入五成雙的方法經反覆驗證其實是錯誤的(上圖就打臉了。。),再次查閱一些資料後,經過官方查證toFixed 原生API如下:


這個方法經過10幾次toFixed驗證後發現都是和結果相吻合的,應該是正確的問題根源所在。
(PS:該錯誤對解決方案無影響)
三、精度問題終極解決方案。
1、四則運算精度問題解決方案
前面提到JS中整型運算是沒有問題的,那麼把浮點數擴大相應的整數倍,轉化成整型在進行計算,計算完縮小為整數倍不就可以解決這個問題了麼。 這個方案確實是可行的,然鵝,怎麼實施這個方案,卻需要琢磨下。 首先第一想到的是乘法,512.06*100就可以轉化為 51206了,然鵝。。

解決方案的思路就是這樣,具體的程式碼寫起來很相對比較複雜,我參考網上的function自己整合了一個npm包,需要的朋友可以自己檢視下原始碼。 總體的思路就是上文介紹的,具體程式碼實現可以參考原始碼(如果能順便點個贊,那最好啦)
2、toFixed解決方案
toFixed的方案相對要好解決,只要有了精度沒問題的四則運算,四捨五入只要直接判斷下浮點數需要精確位數是否大於5即可,具體的程式碼實現也可以參考原始碼。
四、總結
解決方案核心的點就在於用字串方式來解決小數點精度問題,雖然實現上有點複雜,需要考慮的情況比較多,但是確實是最穩妥的做法。如果有需要高精度業務場景的小夥伴可以直接install我封裝的npm包,就可以直接用啦,教程參考github哦!
最後,如果這篇文章對你有點點幫助的話,歡迎動動滑鼠點個贊哦!
(純屬個人手動打字,有手誤或者技術錯誤的地方,肯定多多指正!)