【Python自學】python 浮點數的誤差以及其解決方式
📘 Python 浮點數誤差:為什麼 0.1 + 0.2 不是 0.3?
在 Python 環境中,輸入以下程式碼:
例子一:0.1 + 0.2
執行結果:
什麼?難道跟我們從小學的數學不一樣嗎?
再看看另外一個例子。
例子二:1 - 9 / 10
執行結果:
蛤?根據我們粗淺但應該還算可靠的數學能力,1 - 9 / 10 應該等於 0.1。
再怎麼樣,也不該跑出 0.09999999999999998 這種看起來很像電腦在碎念的數字。
例子三:直接印出 1 / 10
執行結果:
???
所以根據例子二和例子三,難道 1 - 9 / 10 不等於 1 / 10 嗎?
🧭 為什麼 float 計算有時候會不準?
Python 的 float 使用的是 IEEE 754 二進位浮點數標準來表示數字。
這件事不是只有 Python 會發生。只要是使用 IEEE 754 浮點數標準的程式語言,都可能遇到類似問題。
- C 會這樣。
- Java 會這樣。
- JavaScript 也會這樣。
- Python 當然也會這樣。
核心原因是:某些十進位小數,無法用二進位精準表示。
🔢 十進位與二進位的差異
我們先從十進位的例子理解。
1 / 3 在十進位中是:
明明是一個很簡單的分數,但用十進位小數表示時,卻會變成無限循環小數。
同樣的道理,0.1 在二進位中也是無限循環小數。
0.1 的二進位表示
0.1 轉成二進位,大概會長這樣:
後面的 0011 會一直重複,就像十進位中的 0.33333... 一樣。
但問題是,電腦不可能儲存無限長的小數。
截斷或近似後的結果
因為電腦只能保存有限位數,所以 0.1 在內部會被存成一個非常接近但不是完全相等的數值。
這就是浮點數誤差的來源。
十進位的 0.1 對人類很簡單,但對使用二進位儲存小數的電腦來說,不一定能剛好表示。
🧪 為什麼 print(1 / 10) 看起來是 0.1?
你可能會問:既然 0.1 其實不是精準的 0.1,那為什麼直接印出 1 / 10 時,結果看起來是 0.1?
這是因為 Python 在顯示浮點數時,會選擇一個比較簡潔、可讀的表示方式。
也就是說,Python 沒有每次都把內部完整近似值全部印出來,而是用一個足夠代表該浮點數的簡短結果顯示給你看。
但如果經過多個浮點數運算,誤差累積或顯示條件不同,就可能露出馬腳。
🛠️ 浮點數誤差怎麼辦?三種常見解法
浮點數誤差很常見,但並不代表我們不能處理。以下整理三種常見方法。
| 方法 | 適合情境 | 重點 |
|---|---|---|
| round() | 一般顯示、日常計算結果 | 把誤差四捨五入掉。 |
| Decimal | 金額、帳務、高精度十進位運算 | 用字串建立 Decimal。 |
| Fraction | 需要保留精確分數的數學計算 | 直接用分數表示。 |
✅ 方法一:使用 round() 四捨五入
最常見也最直覺的方法,就是用 round() 將結果四捨五入。
執行結果:
因為誤差通常出現在很後面的小數位,對一般日常計算或顯示結果來說,只要把後面的誤差四捨五入掉,通常就夠用了。
round() 適合什麼情況?
- 顯示計算結果。
- 一般數學運算。
- 不需要極高精度的日常程式。
如果是金額、帳務或高精度計算,不建議只靠 round()。
✅ 方法二:使用 Decimal 處理高精度十進位
如果需要處理金額、帳務或比較精確的十進位計算,可以使用 Python 的 decimal 模組。
執行結果:
為什麼 Decimal 可以,float 不行?
因為 Decimal 使用十進位,也就是 base 10 的方式來儲存與計算。
當它收到字串時,可以逐位解析文字,並建立精確的十進位表示。
重要提醒:Decimal 要用字串建立
建議這樣寫:
不建議這樣寫:
因為 0.1 在傳進 Decimal 之前,就已經先被 Python 當成 float 處理過了。
可能會看到類似結果:
使用 Decimal 時,請優先用字串建立數字,例如 Decimal("0.1")。
✅ 方法三:使用 Fraction 進行分數計算
如果你想保留精確分數,而不是進入小數世界,可以使用 fractions 模組中的 Fraction。
執行結果:
直接用分數計算,就不需要把數字轉成小數,也就不會遇到十進位小數轉二進位浮點數時造成的誤差。
Fraction 適合什麼情況?
- 數學題需要保留分數形式。
- 不希望結果變成小數。
- 想進行精確的有理數運算。
這讓人想到以前學數學時,國小常常用小數表示答案;上了國中後,老師反而會要求很多答案用分數表示。
至於跟浮點數誤差有沒有直接關係,不一定。但分數表示確實可以在很多情況下保留精確結果。
📋 三種解法比較表
最後用表格整理 round()、Decimal、Fraction 的差異。
| 方法 | 範例 | 優點 | 適合用途 |
|---|---|---|---|
| round() | round(0.1 + 0.2, 2) | 簡單、快速、直覺 | 一般顯示結果 |
| Decimal | Decimal("0.1") | 適合十進位精確計算 | 金額、帳務、高精度小數 |
| Fraction | Fraction(1, 10) | 保留精確分數 | 數學運算、分數結果 |
🧠 小補充:不要直接比較浮點數是否相等
因為浮點數可能有微小誤差,所以實務上不建議直接這樣比較:
執行結果可能是:
比較好的做法是允許一個很小的誤差範圍,或使用 Python 的 math.isclose()。
執行結果:
✅ 小結
Python 的浮點數誤差不是 bug,而是二進位浮點數表示方式造成的結果。
- float 使用 IEEE 754:用二進位浮點數表示小數。
- 0.1 在二進位中是無限循環小數:電腦只能儲存近似值。
- 0.1 + 0.2 可能不是剛好 0.3:因為內部近似值計算後會出現微小誤差。
- round():適合一般顯示結果。
- Decimal:適合金額、帳務、高精度十進位計算。
- Fraction:適合需要保留精確分數的數學運算。
- math.isclose():適合比較兩個浮點數是否足夠接近。
浮點數不是不會算,而是它用二進位近似表示小數,所以某些十進位小數無法被完美儲存。
留言
張貼留言