JS四則運算與四捨五入精度問題及解決方案

1、Javascript精度問題業務背景

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

京東
雖然看起來只有0.01的差異,可是若是消費者用來投訴甚至打官司,這徹底能夠成爲欺詐消費者的理由。緣由就是JS裏「該死」的toFixed方法。

2、剖析Javascript精度問題緣由

一、超大數和浮點數四則運算精度問題緣由

在剖析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()精度問題

toFixed問題其實很簡單,就是瀏覽器的坑。 es5

圖片描述

圖片描述

能夠看到,在IE和chrome兩個瀏覽器下面,一個簡單的四捨五入,顯示的結果徹底不一樣。就是由於不一樣的瀏覽器廠商對於四捨五入沒有統一的標準,就讓咱們這些開發人員踩坑。。 在IE裏,toFixed就是採用常規的四捨五入方法,然鵝,在chrome倒是採用金融界的更爲精確的四捨六入五成雙方法,雖然這個方法更爲科學,更爲精確,可是卻畢竟大多數人不是金融學家也不是科學家,仍是四捨五入更容易被常人所接受。。。

更正:

在toFixed精度問題上直接參考了大部分網絡解釋,欠缺實踐,沒有深刻考究,上面四捨六入五成雙的方法經反覆驗證實際上是錯誤的(上圖就打臉了。。),再次查閱一些資料後,通過官方查證toFixed 原生API以下:

toFixed Api
大概意思就是若是toFixed的入參小於10的21次方,那麼就 取一個整數n,讓n*10^f - x 的精確值儘量的趨近於0,若是存在兩個這樣的n,取較大的n

toFixed
上圖例子中1.335.toFixed(2)按照四捨六入五成雙應該是1.34,可是實際狀況確實1.33。而用官方的API就能夠解釋爲何是1.33,由於n=133的時候讓n*10^f - x更趨近於0(雖然結果又出現了四則運算的精度問題,可是確實是符合規則的),因此最後獲得的結果是1.33。

這個方法通過10幾回toFixed驗證後發現都是和結果相吻合的,應該是正確的問題根源所在。

(PS:該錯誤對解決方案無影響)

3、精度問題終極解決方案。

一、四則運算精度問題解決方案

前面提到JS中整型運算是沒有問題的,那麼把浮點數擴大相應的整數倍,轉化成整型在進行計算,計算完縮小爲整數倍不就能夠解決這個問題了麼。 這個方案確實是可行的,然鵝,怎麼實施這個方案,卻須要琢磨下。 首先第一想到的是乘法,512.06*100就能夠轉化爲 51206了,然鵝。。

圖片描述
有點坑,由於浮點數的計算自己就會有問題,顯然想簡單的經過乘法來實現整型轉化,是不靠譜的,這時候就要考慮本身實現一個萬無一失的整型轉化方法。 將數字當作字符串,手動的進行小數點移位,這個方案把浮點型轉換爲整型是徹底可行的,接下來就考慮怎麼實現的問題。 由於在JS裏, 超過必定大小的number就會被JS自動轉化爲科學計算法,因此在考慮把number當成字符串處理時。必定要考慮到這一點。

解決方案的思路就是這樣,具體的代碼寫起來很相對比較複雜,我參考網上的function本身整合了一個npm包,須要的朋友能夠本身查看下源碼。 整體的思路就是上文介紹的,具體代碼實現能夠參考源碼(若是能順便點個贊,那最好啦)

二、toFixed解決方案

toFixed的方案相對要好解決,只要有了精度沒問題的四則運算,四捨五入只要直接判斷下浮點數須要精確位數是否大於5便可,具體的代碼實現也能夠參考源碼

4、總結

解決方案核心的點就在於用字符串方式來解決小數點精度問題,雖然實現上有點複雜,須要考慮的狀況比較多,可是確實是最穩妥的作法。若是有須要高精度業務場景的小夥伴能夠直接install我封裝的npm包,就能夠直接用啦,教程參考github哦!

最後,若是這篇文章對你有點點幫助的話,歡迎動動鼠標點個贊哦!

(純屬我的手動打字,有手誤或者技術錯誤的地方,確定多多指正!)

相關文章
相關標籤/搜索