首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

浮點數陷阱:為什麼 100 個 0.1 相加不等於 10?IEEE 754 深度解析與避坑指南

     編輯:關於C語言

現象重現

float sum = 0;
for (int i = 0; i < 100; i++) sum += 0.1f;
printf("%f\n", sum);  // 輸出 10.000002

程式邏輯無誤,結果卻偏離預期。根因在於二進制無法精確表示十進位 0.1。

二進制小數表示原理

十進位小數轉二進制:乘 2 取整數部分。
0.1 × 2 = 0.2 → 0
0.2 × 2 = 0.4 → 0
0.4 × 2 = 0.8 → 0
0.8 × 2 = 1.6 → 1
0.6 × 2 = 1.2 → 1
0.2 × 2 = 0.4 → 0 ... 循環
結果:0.1₁₀ = 0.0001100110011...₂(1100 無限循環)。

IEEE 754 單精度 (float) 佈局

欄位位數說明
符號位10=正,1=負
指數位8Excess-127 編碼(實際指數 = 儲存值 - 127)
尾數位23隱含首位 1,實際精度 24 位

儲存值 = (-1)^符號 × 1.尾數 × 2^(指數-127)

0.75 儲存範例

0.75₁₀ = 0.11₂ = 1.1 × 2^(-1)
符號=0, 指數=126(01111110), 尾數=1000...0
記憶體:0 01111110 10000000000000000000000

為何 0.1 無法精確儲存

  • 尾數僅 23 位,無法容納無限循環的 1100。
  • 截斷/捨入產生微小誤差(約 1.49e-9)。
  • 累加 100 次,誤差放大至 ~2e-6,顯示為 10.000002。

雙精度 稍好但本質相同

double 尾數 52 位,誤差 ~1e-16,100 次累加誤差 ~1e-14,通常列印為 10.000000000000002。依然不精確。

工程避坑三大策略

  1. 整數化計算(金融首選):
    將金額以「分」為單位儲存為 int64_t,運算全程整數,顯示時除 100 插入小數點。
  2. 定點數/BCD 庫:使用 libfixmath、decNumber 等專用庫。
  3. 容差比較:
    if (fabs(a - b) < EPS) ... 而非 a == b。EPS 視量級設定(如 1e-9)。

總結

浮點數是近似值而非精確值。理解 IEEE 754 編碼、二進制循環小數本質、誤差累積機制,並在涉及金融、科學計算時採用整數化或專用庫,是每位 C/C++ 開發者的必修課。

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