0.1 + 0.2 不等於 0.3 的原理以及解決方案的原理描述


問題描述

console.log(0.1 + 0.2);

//0.30000000000000004

原理

浮點數在計算機中的表示是用階碼與尾數結合的形式。

0.75 的二進制表現模式就是(1 + 1 * 2 ^ -1) * 2 ^ -1,為了看得直觀,這里表達式中的2沒有用10來表示

二進制中2 ^ -1表示的其實是十進制中的0.5。想想十進制中冪次方多1代表數值10倍,那么二進制中冪次方多1代表數值2倍。

0.75 在二進制中是可以妥善表現出來的。

var num = 0.75;
num.toString(2);
// 0.11

而0.1在二進制中卻是一個表現不出來的無限不循環數,所以只能取一個近似數。

而計算機精度有限,所能表現的值而非真正的0.1,0.2,所以自然相加時有偏差

解決方案

0.1 + 0.2 // 0.30000000000000004
(0.1 + 0.2).toFixed(2) // "0.30"
parseFloat((0.1 + 0.2).toFixed(2)) // 0.3

24.99 * 10000 // 249899.99999999997
parseFloat((24.99 * 10000).toFixed(2)); // 249900

But why ? see es6文檔 20.1.3.3 Number.prototype.toFixed ( fractionDigits )

toFixed 設計標准介紹

那么問題來了,為何toFixed能夠解決精度問題,而非簡單的省卻小數點做法,也非純字符串操作並處理進位問題。

翻開es6文檔 20.1.3.3 Number.prototype.toFixed ( fractionDigits ) 有答案

注意最為關鍵的一步

Number.prototype.toFixed ( fractionDigits )

1. Let x be thisNumberValue(this value).
2. ReturnIfAbrupt(x).
3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0).
...
7. Let s be the empty String.
8. If x < 0, then
    a. Let s be "-".
...
10. Else x < 10^21,
    a. Let n be an integer for which the exact mathematical value of n ÷ 10f – x is as close to zero as possible. If there are two such n, pick the larger n.
    b. If n = 0, let m be the String "0". Otherwise, let m be the String consisting of the digits of the decimal representation of n
...

11. Return the concatenation of the Strings s and m

代入 (0.1 + 0.2).toFixed(2) // "0.30"

1. 令 x = 0.1 + 0.2 = 0.30000000000000004
3. 令 f = 2,代表保留兩位小數
...
7. 設 s 為空字符串
8. x 不小於 0,這里的 s 還是空字符串
...
10. x < 10^21
    a. 設 n,n / 10^2 - 0.30000000000000004  盡可能為0,n 約等於 30.0000000000000004,取30。
    b. 若 n 為 0,設 m "0",否則 m 為 "30"
    c. (解釋起來很麻煩,總之這里其實是補0操作,最后得到的 m 為 0.30)

...

11. 返回的 s 為空字符串, m 是 "0.30",所以結果就是 0.30

另外注意標准里 fractionDigits 小於 0 或大於 20 時會拋出 RangeError 錯誤,而在 Chrome 62 版瀏覽器中筆者親測小於 0 或大於 100 才會拋出

(0.1 + 0.2).toFixed(101)
VM586:1 Uncaught RangeError: toFixed() digits argument must be between 0 and 100
    at Number.toFixed (<anonymous>)
    at <anonymous>:1:13


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM