PHP右移负数是算术右移,符号位扩展补1,结果恒为负数;-1右移任意位仍为-1;PHP 8+对非法位移抛ValueError,推荐用abs、掩码或除法替代。

PHP右移负数会保留符号位,高位补1
PHP的 >> 是算术右移,不是逻辑右移。对负数执行 >> 时,符号位(最高位)不会丢弃,而是不断用 1 填充左侧空出的位置,导致结果始终为负数,且容易卡在 -1。
典型现象:echo -1 >> 1; 输出 -1,echo -5 >> 2; 可能输出 -2 或 -1(取决于系统位宽和补码截断),但绝不会变成正数或 0 —— 因为符号位被持续扩展。
为什么 -1 >> n 总是 -1
因为 -1 的补码在 32/64 位系统上全是 1(如 0xFFFFFFFF 或 0xFFFFFFFFFFFFFFFF)。每次右移,左边补 1,整体仍全为 1,解释回十进制就是 -1。
- 这和正数右移不同:比如
8 >> 2是2,因为高位补 0 - 它不是“除以 2 的 n 次方再向下取整”,而是补码层面的位移动作
- 想模拟无符号右移?必须手动清除高位符号扩展影响,例如用
& 0x7FFFFFFF(32 位)或& 0x7FFFFFFFFFFFFFFF(64 位)
PHP 8+ 对非法位移会抛出 ValueError
移位位数不能为负,也不能 ≥ 当前整数位宽(如 64 位系统上 >> 64 位)。
立即学习“PHP免费学习笔记(深入)”;
以下代码在 PHP 8.0+ 中直接报错:
var_dump(-5 >> -1); // ValueError: Bit shift operand must be non-negative var_dump(-5 >> 64); // ValueError: Bit shift operand must be less than integer width
而 PHP 7.x 及更早版本对这类操作返回 0,行为不一致,升级后务必检查。
实际处理负数位移的推荐做法
除非你明确在实现底层协议解析或密码学逻辑,否则应避免对负数直接使用 >>。更安全的做法是:
- 先用
abs($n)转为非负数,再移位(仅适用于你只关心数值大小的场景) - 用掩码截断后再移位,例如
($n & 0xFFFFFFFF) >> 1强制按 32 位无符号处理 - 改用除法:对于大多数业务逻辑,
(int)($n / (2 ** $shift))更直观、可读、跨平台 - 若需精确模拟无符号右移,封装函数如
u_right($n, $bits),内部先转为 uint32/uint64 再移位
补码细节和符号扩展是隐式发生的,看不见的高位填充才是最常被忽略的复杂点。



















