负数绝对值可用位运算实现:对非INT_MIN的x,abs(x)等价于~x+1;INT_MIN需单独处理,因其补码取反加一仍为自身;推荐用mask=x>>31后(x+mask)^mask消除分支。

用位运算直接取反加一实现负数绝对值
整数的绝对值本质是“如果是负数就变正,否则不变”,而负数在补码表示下,abs(x) 等价于 x 取反后加 1(即求补),再按符号位逻辑修正。但要注意:这不是对所有 x 都能直接 ~x + 1 —— 因为 INT_MIN 的补码无法用正数表示,~INT_MIN + 1 == INT_MIN,会溢出。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 对 32 位有符号整数,先判断是否为
INT_MIN(即0x80000000),单独返回它(严格来说,abs(INT_MIN)未定义,但多数平台返回自身) - 其余情况:若
x ,用 <code>~x + 1;否则直接返回x - 避免无条件写
(x >> 31) ? (~x + 1) : x——右移符号位扩展依赖平台,C++ 中对负数右移是实现定义,不可靠
用条件表达式避免分支预测失败(适合嵌入式或性能敏感场景)
现代 CPU 对分支预测失败惩罚较大,尤其在循环中反复调用 abs。用位操作消除 if 判断更稳定。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 对
int x,可写:int mask = x >> 31;(注意:仅在支持算术右移的编译器/平台安全,如 GCC/Clang 在 x86-64 下默认如此) - 然后:
return (x + mask) ^ mask;—— 这利用了:当x >= 0,mask == 0,结果为x;当x ,<code>mask == -1(全 1),(x + (-1)) ^ (-1) == ~x + 1 - 该写法不依赖
std::abs,也不显式分支,但需确认目标平台右移行为;若不确定,优先用带 if 的版本
处理 long long 时不能简单改右移位数
把 int 版本改成 x >> 63 看似合理,但 C++ 标准规定:对 long long,右移位数必须 ≤ 类型宽度减 1,且 63 在某些 64 位平台可能超出 sizeof(long long) * CHAR_BIT - 1(比如某些嵌入式平台 long long 是 128 位)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
sizeof(T) * CHAR_BIT - 1动态计算位宽,例如:constexpr int shift = sizeof(long long) * 8 - 1; - 更稳妥做法:统一用
x —— 现代编译器(GCC/Clang/MSVC)对这种简单三元会自动优化为条件移动指令(CMOV),无分支且安全 - 不要手动写
~x + 1处理long long,因为-x语义清晰且编译器优化充分
浮点数绝对值不能用整数位运算
IEEE 754 单/双精度浮点数的符号位在最高位,但指数和尾数布局复杂,直接位操作极易出错(比如 NaN、Inf、次正规数)。哪怕你把 float 强转成 uint32_t 再清符号位,也绕不开特殊值处理。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 对
float或double,最安全方式就是x —— 编译器识别此模式后,通常生成 <code>ANDPS/ANDPD指令(x86)或fabs指令(ARM),比手写位运算更快更正确 - 若真要位操作(如裸机环境无 FPU 支持),必须先检查是否为
NaN(通过bits & 0x7FFFFFFF != 0x7FFFFFFF等),否则清符号位后仍可能产生非法值 - 标准库的
fabsf/fabs底层就是这么做的,自己重写没优势
真正难的不是“怎么写”,而是判断什么时候该信任编译器、什么时候必须手动控制位——比如在确定输入永远是规约正数且无异常值的 DSP 场景,位操作才有意义;其他时候,x 就是最简、最稳、最易读的选择。


















