現象重現
float x = 67.8564f, y = -789.124f;
printf("%f, %f\n", x, y);
// 輸出: 67.856400, -789.124023
根本原因:IEEE 754 二進位浮點表示
float(單精度)佔 32 位元:1 位號碼 + 8 位指數 + 23 位尾數(實際 24 位有效位)。
- 十進位
0.124 在二進位是無限循環小數:0.00011111101111100111011...。
- 23 位尾數只能近似儲存,捨入產生誤差
~1e-7。
printf("%f") 預設印 6 位小數,將近似值展開顯示。
解決方案對比
| 方案 | 程式碼 | 適用場景 | 優缺點 |
改用 double | double y = -789.124; | 通用科學計算、座標、物理模擬 | 精度約 15 位十進位,記憶體雙倍,速度現代 CPU 無差異 |
| 輸出格式限制位數 | printf("%.3f", y); | 僅顯示、不後續計算 | 不改變儲存值,後續運算仍攜帶誤差 |
| 定點數/整數分 | int y_cents = -789124; | 金融、貨幣、計價系統 | 精確無誤差,需自行處理小數點顯示 |
| 十進位浮點庫 | _Decimal32 (GCC) / decimal (C23) | 銀行級精確十進位運算 | 硬體支援有限、效能較低、編譯器支援不一 |
最佳實踐建議
- 預設用
double,除非記憶體極度受限(嵌入式)或陣列極大(GPU 顯存)。
- 金融/會計 禁用二進位浮點,改用整數分或十進位庫。
- 比較浮點數相等時,用
fabs(a-b) < EPS,EPS = 1e-9 (double) 或 1e-6 (float)。
- 列印除錯時用
%.10f 或 %.17g 觀察真實儲存值。