機器人控制、圖形學、導航算法中頻繁需將任意角度 (如 361°、-720.5°) 映射到標準區間 [-180, 180) 或 [0, 360)。本文對比多種實現方案,推薦無分支數學公式,附帶邊界測試用例與性能分析。
angle (度數)result ∈ [-180, 180)angle ≡ result (mod 360)361 → 1, -361 → -1, 270 → -90, -270 → 90, 180 → -180 (或 180,視具體定義)double normalize_branch(double angle) {
angle = fmod(angle, 360.0); // [-360, 360]
if (angle < 0) angle += 360.0; // [0, 360)
if (angle >= 180.0) angle -= 360.0; // [-180, 180)
return angle;
}
// 核心公式:映射到 [-180, 180)
double normalize_math(double angle) {
// 步驟 1:取模到 [0, 360) 或 (-360, 360]
angle = fmod(angle, 360.0);
if (angle < 0) angle += 360.0; // 確保 [0, 360)
// 步驟 2:無分支映射到 [-180, 180)
// 原理:x ∈ [0,360) → x/180 ∈ [0,2) → floor(x/180) ∈ {0,1}
// n = floor(x/180) → x - n*360 將 [180,360) 映射到 [-180,0)
double n = floor(angle / 180.0);
return angle - n * 360.0;
}
// 更緊湊版 (利用 fmod 特性,但需注意負數行為)
double normalize_compact(double angle) {
angle = fmod(angle + 180.0, 360.0); // 先平移 180,再取模
if (angle < 0) angle += 360.0; // 確保 [0, 360)
return angle - 180.0; // 平移回 [-180, 180)
}
// 輸入輸出均為整數度數 (int32_t)
int32_t normalize_int(int32_t angle) {
angle %= 360; // C 語言 % 對負數結果為負或零 [-359, 359]
if (angle < 0) angle += 360; // [0, 359]
// 無分支:angle >= 180 ? angle - 360 : angle
// 利用符號位:(angle - 180) >> 31 得到全 1 (負) 或全 0 (非負)
int32_t mask = (angle - 180) >> 31; // 算術右移保持符號位
return angle - (mask & 360);
}
assert(normalize(0) == 0);
assert(normalize(180) == -180); // 或 180,文檔化定義
assert(normalize(-180) == -180);
assert(normalize(360) == 0);
assert(normalize(-360) == 0);
assert(normalize(361) == 1);
assert(normalize(-361) == -1);
assert(normalize(270) == -90);
assert(normalize(-270) == 90);
assert(normalize(540) == -180);
assert(normalize(-540) == -180);
assert(normalize(720) == 0);
assert(normalize(-720) == 0);
// 浮點精度測試
assert(fabs(normalize(180.0000001) - (-179.9999999)) < 1e-6);
在 x86-64 GCC -O3 下,10 億次調用:
MathUtils::normalizeDeg180() normalizeDeg360() normalizeRadPi(),避免重複造輪子。180° → -180° 還是 180°,團隊統一認知。