原文: http://bits.stephan-brumme.com/absInteger.html

这段代码用位运算计算整数的绝对值:

int myAbs(int x)
{
  const int bit31 = x >> 31;
  return (x ^ bit31) - bit31;
}

它的核心思路是:先根据符号位生成一个掩码 bit31,再用同一条表达式同时处理正数和负数。

符号掩码

x >> 31 依赖的是 算术右移(arithmetic right shift):右移时,左边空出的位不是随便填 01,而是用符号位填充,以保持数值的符号不变(右移一位相当于除以 2)。

右移类型左边补什么适用
算术右移符号位(正数补 0,负数补 1有符号 int
逻辑右移始终补 0无符号 unsigned

在常见的 32 位补码整数中,最高位(第 31 位)就是符号位:

  • x >= 0 时,符号位为 0;右移 31 次,左边每次补的都是 0,最终 32 位全 0,结果是 0
  • x < 0 时,符号位为 1;右移 31 次,左边每次补的都是 1,最终 32 位全 1

负数看起来是「补 1」,本质上是符号位本来就是 1,算术右移只是在复制符号位。右移 31 次后全 1,在补码里这个位模式恰好表示 -1

x = -5  (32 位补码,省略中间位)
  ...11111111 11111011
>> 1  (左边补符号位 1)
  ...11111111 11111101   // -3,符号保持不变
>> 31 (符号位 1 不断复制到左边)
  11111111 11111111      // 32 位全 1 = -1

所以 bit31 只有两种值:

x 的符号bit31
非负数0
负数-1

表达式如何工作

最终返回值是:

(x ^ bit31) - bit31

x 是非负数时:

  • bit31 = 0
  • x ^ 0 仍然是 x
  • x - 0 仍然是 x

x 是负数时:

  • bit31 = -1,二进制全是 1
  • x ^ -1 等价于按位取反,也就是 ~x
  • ~x - (-1) 等价于 ~x + 1

而在补码表示中,~x + 1 正好是 -x。因此负数会被转换成对应的正数。

示例

x = -5 为例,使用 8 位二进制方便观察:

x        = 11111011   // -5 的补码
bit31    = 11111111   // -1,全 1 掩码
x ^ bit31 = 00000100  // 按位取反,得到 4
再减 bit31 = 00000100 - 11111111
          = 00000100 + 00000001
          = 00000101  // 5

所以 myAbs(-5) 返回 5

需要注意的限制

这段写法适合理解补码和位运算技巧,但在 C 语言里有几个前提:

  • 它假设 int 是 32 位,所以用 x >> 31 取符号位。
  • 它依赖 int 右移为算术右移(左边补符号位);C 标准对负数右移的结果是实现定义行为,但 x86、ARM 等常见平台均符合上述行为。
  • 它无法正确表示 INT_MIN 的绝对值,因为 INT_MIN 的正数结果超出了 int 能表示的范围。

因此,面试或学习时可以用它解释位运算技巧;工程代码里应优先选择更清晰、边界行为更明确的实现。