Chuck Allison
浮點數不是「實數」#
浮點數(floating-point numbers)在數學意義上不是「實數(real numbers)」,儘管某些程式語言(如 Pascal 和 Fortran)稱它們為 real。實數具有無限精度且連續無損失;浮點數的精度有限,它們是有限的,類似於「行為不良的整數」,因為它們在整個範圍內並非均勻分布。
精度的間距問題#
舉例說明:將 2147483647(32 位元有號整數最大值)賦值給一個 32 位元的 float 變數 x 並印出,你會看到 2147483648。印出 x-64,仍然是 2147483648。但印出 x-65,你會得到 2147483520!因為在該範圍內,相鄰浮點數的間距是 128。
IEEE 浮點數是基於二進位的固定精度數字,使用科學記號表示法:1.d₁d₂...d_{p-1} × 2ᵉ,其中 p 是精度(float 為 24,double 為 53)。兩個連續數字之間的間距為 2^(1-p+e),可以用 machine epsilon(ε = 2^(1-p))近似為 ε|x|。
了解浮點數鄰近區域的間距,可以避免經典的數值錯誤。例如在迭代計算中,確保你要求的容差不小於該區域的間距,否則程式會無限循環。
捨入誤差(Roundoff)與災難性消去(Catastrophic Cancellation)#
由於浮點數是實數的近似值,不可避免地存在少量誤差,稱為捨入誤差(roundoff)。當你相減兩個近乎相等的數字時,最高有效位數互相消去,原本在最低有效位的捨入誤差被提升到最高有效位,汙染後續所有計算——這稱為抹消現象(smearing)。你需要仔細檢視演算法以防止災難性消去(catastrophic cancellation)。
例如用二次方程式公式解 x² - 100000x + 1 = 0 時,可以改用另一種計算方式來避免此問題。
實務建議#
- 計算 eˣ 的函式庫若對大的負數 x 天真地使用
1 + x + x²/2 + x³/3! + ...,可能會因為正負項交替導致捨入誤差累積到正無窮大。解法很簡單:對負數 x,改為計算eˣ = 1/e^|x| - 財務應用不應使用浮點數——應使用 Python 和 C# 等語言中的 decimal 類別
- 浮點數是為了高效科學計算而設計的,但效率在準確度面前毫無意義
記住捨入誤差的來源,並據此撰寫程式碼!