直接計算 (a + b) / 2 若 a+b 溢出會導致錯誤。本文介紹三種無溢出算法,適用於有符號/無符號整數、浮點數。
int avg_diff(int a, int b) {
return a + (b - a) / 2; // 等價於 (a+b)/2 但無溢出風險
}
原理:a + (b-a)/2 = (2a + b - a)/2 = (a+b)/2。b-a 不會溢出(同符號時差值絕對值較小)。
unsigned int avg_bit(unsigned int a, unsigned int b) {
return (a & b) + ((a ^ b) >> 1);
}
原理:a+b = (a&b)<<1 + (a^b),平均值右移 1 位。(a&b) 為進位,(a^b) 為不進位和。
double avg_double(double a, double b) {
return (a + b) / 2.0; // 雙精度範圍極大,實際無溢出風險
}
原代碼:
c = (a - b) / 2.0; if (c <= 0) sum = b + c; else sum = c + b;
等價於 b + (a-b)/2 = (a+b)/2,邏輯正確但分支多餘。簡化為 a + (b-a)/2 更清晰。
#include <stdio.h>
#include <limits.h>
int avg_safe(int a, int b) {
return a + (b - a) / 2;
}
int main() {
// 測試極值
printf("INT_MAX=2147483647, INT_MIN=-2147483648\n");
printf("avg(INT_MAX, INT_MAX) = %d\n", avg_safe(INT_MAX, INT_MAX)); // 2147483647
printf("avg(INT_MIN, INT_MIN) = %d\n", avg_safe(INT_MIN, INT_MIN)); // -2147483648
printf("avg(INT_MAX, INT_MIN) = %d\n", avg_safe(INT_MAX, INT_MIN)); // -1
printf("avg(100, 200) = %d\n", avg_safe(100, 200)); // 150
return 0;
}
| 方法 | 適用類型 | 優點 | 缺點 |
|---|---|---|---|
| 差值法 | 所有整數、浮點數 | 通用、簡潔、無分支 | 需注意除法截斷方向 |
| 位運算法 | 無符號整數 | 無除法、硬件友好 | 有符號數需調整 |
| 浮點轉換 | 浮點數、小整數 | 直觀 | 大整數精度損失 |