原始题目
以下代码的输出是什么?
int x = -5;
x = x >> 1;
cout << x;
- A. -2
- B. -3
- C. -5
- D. 2
答案:B(-3)
为什么是 -3,不是 -2
x >> 1 对负数是算术右移:右移时高位补符号位 1(而不是补 0),等价于向下取整的除以 2:
-5 / 2 = -2.5 → 向下取整(floor)= -3
所以 x >> 1 = floor(-5 ÷ 2) = -3。
补码位级演示
-5 = 11111011 (8位补码示意)
>>1 = 11111101 ← 最高位补符号位 1,低位 1 被挤掉
= -3
32 位同理:
-5 = ...11111011
>>1 = ...11111101 = -3
最大陷阱:右移 ≠ 整数除法
很多人用除法直觉算出 -2(-5 / 2 = -2),于是选 A——错了。
| 表达式 | 结果 | 取整方向 |
|---|---|---|
-5 >> 1 |
-3 | 向下(floor,往 -∞) |
-5 / 2 |
-2 | 向零(trunc,往 0) |
5 >> 1 |
2 | 同 5/2,两者一致 |
6 >> 1 |
3 | 同 6/2,两者一致 |
-6 >> 1 |
-3 | 同 -6/2,偶数无差别 |
结论:只有负数且为奇数时,
>> 1和/ 2才差 1。
通用规律
\[x \gg n = \left\lfloor \frac{x}{2^n} \right\rfloor\]
对正数、负数、零都成立。
7 >> 1 // 3 floor(3.5)=3
-7 >> 1 // -4 floor(-3.5)=-4
-7 / 2 // -3 trunc(-3.5)=-3
-8 >> 2 // -2 floor(-8/4)=-2
关于“实现定义”
- C++20 之前:标准把有符号数右移留作实现定义,但所有主流编译器(GCC / Clang / MSVC)都实现为算术右移(补符号位)。
- C++20 起:标准明确规定有符号右移就是算术右移,
x >> n即 \(x / 2^n\) 向下取整。
竞赛笔试中一律按算术右移处理,直接套用 floor(x / 2ⁿ)。
考点总结(CSP-J 初赛选择题)
- 右移 n 位 ≈ 除以 \(2^n\),但对负奇数是向下取整,比
/少 1。 - 正数、偶数:右移与除法结果相同,可以放心用
>> 1代替/ 2(还能加速)。 - 算术右移补符号位,逻辑右移补 0(无符号数
unsigned才是逻辑右移)。 - 秒杀技巧:题干出现负数右移,先算
x / 2,若 x 是负奇数再把结果减 1。
思考题
unsigned int u = -5;(即 \(2^{32}-5\)),u >> 1 的结果是多少?
答案
2147483645。因为 unsigned 是逻辑右移,高位补 0:\((2^{32}-5) / 2 = 2147483645.5\) 向下取整 = 2147483645。和 int 的 -3 是完全不同的世界。



