浮點數很奇怪

幾乎每個程式設計師都犯的第一個錯誤就是假設這段程式碼按預期工作:

float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
    total += a;
}

新手程式設計師假設這將總結 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99 範圍內的每一個數字,以產生結果 199-數學上正確的答案。

發生兩件事使這不真實:

  1. 書面的程式永遠不會結束。a 永遠不會等於 2,迴圈永遠不會終止。
  2. 如果我們改寫迴圈邏輯來檢查 a < 2,則迴圈終止,但總數最終與 199 不同。在符合 IEEE754 標準的機器上,它通常總結為大約 201

發生這種情況的原因是浮點數表示其指定值的近似值

經典的例子是以下計算:

double a = 0.1;
double b = 0.2;
double c = 0.3;
if(a + b == c)
    //This never prints on IEEE754-compliant machines
    std::cout << "This Computer is Magic!" << std::endl; 
else
    std::cout << "This Computer is pretty normal, all things considered." << std::endl;

雖然我們程式設計師看到的是用 base10 編寫的三個數字,但編譯器(和底層硬體)看到的是二進位制數。因為 0.10.20.3 需要通過 10 完美劃分 - 這在基礎 10 系統中非常容易,但在 base-2 系統中是不可能的 - 這些數字必須以不精確的格式儲存,類似於 1/3 的數字。在基地 10 中以不精確的形式儲存在 0.333333333333333... 中。

//64-bit floats have 53 digits of precision, including the whole-number-part.
double a =     0011111110111001100110011001100110011001100110011001100110011010; //imperfect representation of 0.1
double b =     0011111111001001100110011001100110011001100110011001100110011010; //imperfect representation of 0.2
double c =     0011111111010011001100110011001100110011001100110011001100110011; //imperfect representation of 0.3
double a + b = 0011111111010011001100110011001100110011001100110011001100110100; //Note that this is not quite equal to the "canonical" 0.3!