為什麼JS中0.1+0.2 != 0.3

LearnInPro發表於2018-06-25

 

為什麼JS中0.1+0.2 != 0.3

 

在我曾經的一篇《 javascript入門教程 (2) 》中,講到JS中數字運算時,我們提到過一個叫做 數字運算中的精度缺失的問題,當時我們只是簡單說了下,並未對其原因做了解。這篇文章,我就帶著大家瞭解下 JS運算中精度的缺失問題

首先我們先來看一個例子

console.log(0.1 + 0.2)  // 結果是0.30000000000000004,而不是3

這裡0.1 + 0.2 != 0.3 這個就是我們要解決的問題了。

要弄清這個問題的原因,首先我們需要了解下在計算機中數字是如何儲存和運算的。在計算機中,數字無論是定點數還是浮點數都是以多位二進位制的方式進行儲存的。在JS中數字採用的IEEE 754的雙精度標準進行儲存,我們可以無需知道他的儲存形式,只需要簡單的理解成就是儲存一個數值所使用的二進位制位數比較多而已,這樣得到的數會更加精確。

這裡為了簡單直觀,我們使用定點數來說明問題。在定點數中,如果我們以8位二進位制來儲存數字。

 

對於整數來說,十進位制的35會被儲存為: 00100011 其代表 2^5 + 2^1 + 2^0
對於純小數來說,十進位制的0.375會被儲存為: 0.011 其代表 1/2^2 + 1/2^3 = 1/4 + 1/8 = 0.375

而對於像0.1這樣的數值用二進位制表示你就會發現無法整除,最後算下來會是 0.000110011....由於儲存空間有限,最後計算機會捨棄後面的數值,所以我們最後就只能得到一個近似值。在JS中採用的IEEE 754的雙精度標準也是一樣的道理,我們且不管這個標準下的儲存方式跟定點數儲存有何不同,單單在這一點上他們都是相同的,也就是儲存空間有限,當出現這種無法整除的小數的時候就會取一個近似值,在js中如果這個近似值足夠近似,那麼js就會認為他就是那個值。(比較拗口,舉個例子)

console.log(0.1000000000000001)  
// 0.1000000000000001 (中間14個0,會列印出它本身)

console.log(0.10000000000000001)  
// 0.1 (中間15個0,js會認為這兩個值足夠接近,所以會顯示0.1)

    所以我們現在應該可以理解,就是說由於0.1轉換成二進位制時是無限迴圈的,所以在計算機中0.1只能儲存成一個近似值。另外說一句,除了那些能表示成 x/2^n 的數可以被精確表示以外,其餘小數都是以近似值得方式存在的。
    在0.1 + 0.2這個式子中,0.1和0.2都是近似表示的,在他們相加的時候,兩個近似值進行了計算,導致最後得到的值是0.30000000000000004,此時對於JS來說,其不夠近似於0.3,於是就出現了0.1 + 0.2 != 0.3 這個現象。
    當然,也並非所有的近似值相加都得不到正確的結果。有時兩個近似值進行計算的時候,得到的值是在JS的近似範圍內的,於是就可以得到正確答案。至於哪些值計算後能得到正確結果,哪些不能,我們也不需要去記。最好的方法就是我們想辦法規避掉這類小數計算時的精度問題就好了。

 

那麼最常用的方法就是將浮點數轉化成整數計算。因為整數都是可以精確表示的。

方法也很簡單,舉個例子:

對於0.1 + 0.02 我們需要轉化成 ( 10 + 2 ) / 1e2
對於0.1 * 0.02 我們則轉化成 1 * 2 / 1e3

 

按照這個思路,寫個簡單的方法就好了。

 

 

另外就是如果你在學習前端的過程中有任何問題想要諮詢,歡迎關注 LearnInPro的公眾號,在上面隨時向我提問哦。?

 

相關文章