首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

浮點數誤差本質與工程應對:為什麼 0.1 迴圈會無限跑?

     編輯:程式設計疑難解答

現場重現

for (double i = 0; i != 10; i += 0.1)
    printf("%.1lf\n", i); // 無限循環!

根本原因:二進制無法精確表示 0.1

0.1 (十進制) = 0.0001100110011... (二進制無限循環)
double 52位尾數截斷後 ≈ 0.10000000000000000555
累加100次誤差累積 → i 永遠 != 10.0

誤差量化標準:機器 epsilon

  • FLT_EPSILON (float) ≈ 1.19e-7
  • DBL_EPSILON (double) ≈ 2.22e-16
  • 相對誤差界:|a-b| <= max(|a|,|b|) * EPSILON

五種工程解決方案

1. 整數計數器法(最推薦)

for (int i = 0; i <= 100; i++) {
    double val = i * 0.1; // 或 val = i / 10.0
    printf("%.1lf\n", val);
}

2. 邊界值判斷

for (double i = 0; i <= 10.0 + 1e-9; i += 0.1) // 留容差
// 或
for (double i = 0; i < 10.0 - 1e-9; i += 0.1)

3. 定點數/整數縮放

// 縮放100倍用整數
for (int i = 0; i <= 1000; i++) {
    printf("%.2f\n", i / 100.0);
}

4. 使用 nextafter 精確步進 (C99)

double i = 0, target = 10.0;
while (i <= target) {
    printf("%.1lf\n", i);
    i = nextafter(i + 0.1, target); // 朝target方向最近的可表示值
}

5. 十進制浮點庫

  • GCC _Decimal64 (十進制浮點)
  • Java BigDecimal
  • Python decimal.Decimal

為什麼「不運行程序無法知道結果」?

這涉及停機問題與浮點數確定性語義:雖然IEEE 754規範了運算行為,但誤差累積路徑依賴於具體指令序列、優化級別、CPU指令集(x87 vs SSE)。編譯器可能將迴圈展開、向量化、使用FMA指令,導致中間結果精度不同。唯有在目標平台實測才能確定具體行為。

必看科普

《浮點數那些事兒》視頻講座

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved