除法運算在不同數值類型上展現截然不同的語意:整數向零截斷、浮點數遵循 IEEE 754 (含 Infinity/NaN)、decimal 提供高精度但無特殊值。掌握這些差異,能避免精度遺失與執行時例外。
三大數值類型除法語意對比
| 特性 | 整數 (int, long...) | 浮點數 (float, double) | decimal |
| 結果類型 | 同運算元 (較大者) | 同運算元 (較大者) | decimal |
| 精度 | 截斷小數部 | ~15-17 位 (double) | 28-29 位有效數字 |
| 除零行為 | DivideByZeroException | ±Infinity / NaN | DivideByZeroException |
| 溢位行為 | 環繞/例外 (視語境) | ±Infinity | 例外 (無 Infinity) |
| 適用場景 | 計數、索引、位元操作 | 科學計算、圖形、物理 | 貨幣、金融、高精度需求 |
整數除法:向零截斷 (非向下取整)
5 / 3 // 1
-5 / 3 // -1 (向零,非 -2)
5 / -3 // -1
-5 / -3 // 1
// 數學關係:a = (a / b) * b + (a % b)
// 餘數符號跟隨被除數
5 % 3 // 2
-5 % 3 // -2
常見陷阱:平均值計算
// 錯誤:整數除法截斷
int avg = (a + b) / 2; // a=2, b=3 → 2 (非 2.5)
// 正確:轉浮點或用中點公式
double avg1 = (a + b) / 2.0;
int avg2 = a + (b - a) / 2; // 避免溢位,向下取整
int avg3 = (a + b) >> 1; // 僅限非負,位運算最快
浮點數除法:IEEE 754 完整語意
特殊值結果表
| 被除數 \ 除數 | 正常值 | +0.0 | -0.0 | +∞ | -∞ | NaN |
| 正常值 > 0 | 正常 | +∞ | -∞ | +0.0 | -0.0 | NaN |
| 正常值 < 0 | 正常 | -∞ | +∞ | -0.0 | +0.0 | NaN |
| +0.0 | +0.0 | NaN | NaN | +0.0 | -0.0 | NaN |
| -0.0 | -0.0 | NaN | NaN | -0.0 | +0.0 | NaN |
| +∞ | +∞ | +∞ | -∞ | NaN | NaN | NaN |
| -∞ | -∞ | -∞ | +∞ | NaN | NaN | NaN |
| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
關鍵屬性
double.IsInfinity(x) // 檢查 ±∞
double.IsNaN(x) // 檢查 NaN (x != x 也可)
double.IsNegative(x) // 檢查負號 (含 -0.0、-∞)
// 正負零區分
1.0 / 0.0 // +∞
1.0 / -0.0 // -∞
Math.Sign(-0.0) // 0 (但 1/-0.0 = -∞)
decimal 除法:銀行家精度、無特殊值
高精度範例
decimal a = 10.0m, b = 3.0m;
decimal result = a / b;
// 3.3333333333333333333333333333 (28-29 位)
decimal c = 1.0m / 7.0m;
// 0.1428571428571428571428571429 (循環小數精確截斷)
除零拋出例外 (無 Infinity/NaN)
decimal d = 1.0m;
var crash = d / 0.0m; // DivideByZeroException
// 檢查模式
decimal SafeDivide(decimal a, decimal b)
=> b == 0 ? 0 : a / b; // 或拋出自訂例外
範圍限制
// decimal 範圍:±1.0 × 10⁻²⁸ 至 ±7.9 × 10²⁸
decimal max = decimal.MaxValue; // 79228162514264337593543950335
decimal min = decimal.MinValue; // -79228162514264337593543950335
max / 0.5m // OverflowException (結果超出範圍)
混合類型除法:隱含轉換規則
// 整數 / 整數 → 整數 (截斷)
int i = 5 / 2; // 2
// 整數 / 浮點數 → 浮點數
double d1 = 5 / 2.0; // 2.5 (2.0 為 double)
float f1 = 5 / 2.0f; // 2.5f
// 長整數 / 整數 → 長整數
long l = 5L / 2; // 2L
// decimal 嚴格:不與 float/double 隱含轉換
decimal dec = 5.0m;
// double x = dec / 2.0; // 編譯錯誤
double x = (double)dec / 2.0; // 明確轉型
效能考量
| 類型 | 相對速度 | 硬體支援 | 備註 |
| 整數 | 最快 (1x) | 專用指令 (DIV/IDIV) | 延遲 10-40 週期 |
| float | ~2-3x | SSE/AVX (單精度) | 向量化友善 |
| double | ~3-5x | SSE/AVX (雙精度) | 標準科學計算 |
| decimal | ~50-100x | 軟體模擬 (無硬體指令) | 128 位整數模擬 |
優化技巧
// 除以常數 2 的冪次 → 位移
x / 2 → x >> 1 (有號)
x / 4 → x >> 2
x / 8 → x >> 3
// 除以常數 → 乘以倒數 (編譯器常自動優化)
x / 10 → x * 0.1 (浮點數)
x / 10 → (x * 0xCCCD) >> 18 (整數近似,需驗證)
最佳實踐總結
- 金融/貨幣絕對使用
decimal — 二進位浮點無法精確表示 0.1
- 整數除法注意截斷方向 — 向零,非向下 (負數不同)
- 浮點數除零不拋出例外 — 必須檢查
IsInfinity/IsNaN
- decimal 除零拋出例外 — 需防禦性檢查
- 避免 decimal 與 float/double 混用 — 無隱含轉換,明確轉型易失精度
- 熱路徑除法優化 — 常數除法轉乘法、2 的冪次轉位移
- 平均值/中點計算防溢位 —
a + (b - a) / 2
理解三大數值類型除法的語意邊界與效能特性,是編寫正確、高效數值計算程式的關鍵。