什麼是浮點數?為什麼 0.1 + 0.2 != 0.3?本文從定點數與浮點數的本質差異切入,詳細解析 IEEE 754 標準下的儲存結構、指數偏移、尾數隱含位,並說明精度誤差產生的根本原因與應對策略。
定點數約定小數點固定在第 N 位,例如第 3 位:
123.456 → 儲存為整數 123456,運算時除以 1000
0.00123456 → 只能表示為 0.001(截斷誤差)
優點:運算速度快(整數運算)、無精度誤差(在範圍內)。
缺點:動態範圍極小,無法同時表示極大與極小數值。
浮點數採用「科學記號法」:
值 = (-1)^符號 × 1.尾數 × 2^(指數-偏移量)
以 32 位元 float 為例(IEEE 754 Single Precision):
| 欄位 | 位數 | 說明 |
|---|---|---|
| 符號位 | 1 bit | 0=正,1=負 |
| 指數位 | 8 bits | 移碼表示,偏移量 127 |
| 尾數位 | 23 bits | 隱含首位 1,實際精度 24 bits |
-1.2345 × 10^-3 → 二進制近似編碼1.2345 × 10^9 → 指數大、尾數相同關鍵權衡:指數位越多 → 範圍越大;尾數位越多 → 精度越高。雙精度 double 為 1+11+52 位元,大幅擴展兩者。
許多十進制小數(如 0.1、0.2)在二進制下是無限循環小數:
0.1₁₀ = 0.0001100110011...₂ (無限循環)
0.2₁₀ = 0.001100110011...₂ (無限循環)
尾數只有 23/52 位,必須捨入,導致 0.1 + 0.2 = 0.30000000000000004。
BigDecimal / 定點數庫。Math.abs(a - b) < EPSILON,而非 a == b。printf("%.2f", val) 避免顯示多餘尾數。double(52 位尾數)降低累積誤差。浮點數是「用有限位元逼近無限實數」的工程妥協。理解其儲存結構與誤差來源,是寫出健壯數值程式的前提。下次看到 0.1 + 0.2 != 0.3,請告訴自己:這不是 Bug,是 IEEE 754 規範下的必然結果。