數學上加法滿足結合律,但在電腦浮點運算中,運算順序直接影響結果。本文透過 C# 實測,揭示整數溢位檢查與浮點精度捨入如何打破結合律,並解答 /checked 編譯選項下的行為差異。
int a = int.MaxValue; // 2,147,483,647
int b = 1;
int c = -a;
Console.WriteLine(a + (b + c)); // 1
Console.WriteLine((a + b) + c); // unchecked: 1 / checked: OverflowException
b + c = 1 + (-MaxValue) = -2,147,483,646(安全)a + b = MaxValue + 1 → 溢位:MinValue (-2,147,483,648),再加 c 得 1OverflowExceptionfloat a = float.MaxValue; // ≈ 3.4e38
float b = -a;
float c = -1.0f;
Console.WriteLine(a + (b + c)); // 0
Console.WriteLine((a + b) + c); // -1
原因分析:
b + c = -MaxValue - 1 ≈ -MaxValue(-1 相對 MaxValue 太小,被捨入)a + (b + c) ≈ MaxValue + (-MaxValue) = 0a + b = 0,再 + c = -1浮點數有效位數有限(float 約 7 位十進位有效數字),大數吃小數導致資訊遺失,順序不同,遺失位置不同,結果自然不同。
答案:仍為 0 和 -1,無異常。
原因: /checked 僅影響整數溢位檢查。浮點運算遵循 IEEE 754 標準,溢位產生 Infinity,精度捨入屬正常行為,不受 checked 影響。
decimal(128 位,28-29 位有效數字)或定點數double(15-16 位有效數字),謹慎重排運算順序(如 Kahan 求和)==,改用 Math.Abs(a-b) < epsilonunchecked 效能佳;安全關鍵路徑啟用 checked 或手動檢查理解運算順序對數值結果的影響,是寫出健壯數值計算程式的必修課。下次見到 (a+b)+c,請三思:順序真的不重要嗎?