> 是底层位操作,实际移位数由右操作数低5位(int)或低6位(long)决定,负移位数被禁止,>> 对负数为算术右移,小类型移位前自动提升为int易致符号扩展。

直接说结论: 和 <code>>> 不是简单的“乘2”或“除2”快捷写法,它们是底层位操作,行为由操作数类型和移位数的**低5位或低6位**决定,超出范围的移位数会被自动截断——这是绝大多数人踩坑的根源。
为什么 i 和 <code>i 结果一样?
因为 i 是 int(32位),C# 规定:实际移位数只取右操作数的**低5位**。33 的二进制是 100001,低5位是 00001(即 1),所以 i 等价于 <code>i 。
- 对
int或uint:移位数 =rightOperand & 0x1F(即 % 32,但不是取模,是位与) - 对
long或ulong:移位数 =rightOperand & 0x3F(即 % 64) - 右操作数为负数?编译不通过——C# 明确禁止负移位数
- 移位后高位溢出的部分直接丢弃,不会抛异常,也不会溢出检查(
checked也无效)
>> 对负数是算术右移,不是逻辑右移
>> 在 C# 中始终是**带符号右移**(arithmetic shift)。对负数,高位补的是符号位(1),不是 0。这意味着它不等价于数学上的整除,尤其在负数场景下结果容易误判。
-
-8 >> 1→ 结果是-4(二进制11111000 >> 1 → 11111100),符合预期 -
-1 >> 3→ 仍是-1(全 1 的补码不断补 1) - 需要无符号右移?用
(uint)x >> n或(ulong)x >> n强制转成无符号类型再移 - 别试图用
>>做“清空高位”或“提取字节”,它依赖符号,行为不稳定
byte/short/char 移位后自动升为 int,小心隐式类型转换
如果你写 byte b = 1; int x = b ,表面上看没问题,但中间过程是:<code>b 先被提升为 int,再左移,结果也是 int。这在组合位运算时极易引发意料外的符号扩展或高位污染。
byte b = 0b1000_0000; int r = b → <code>r == -256,不是256!因为b提升为int后是0x00000080,左移 1 变成0x00000100(256),但若你误以为它还是 byte 范围,就错了- 想保持低位字节操作?显式用
(byte)(b 截断,或直接用 <code>ushort/uint避免符号干扰 - 涉及多个小整型拼包(如协议解析)时,优先统一用
uint或int,避免反复 cast
最常被忽略的一点:移位数本身不参与类型推导,它必须是 int 或可隐式转为 int 的类型;而左操作数类型直接决定“取多少位做移位计数”——这个规则藏在 IL 层,运行时不会报错,只会静默截断。写位运算前,先盯住两个操作数的类型。










