~运算符结果并非总是负数,但对正整数取反后因64位补码表示导致符号位为1,故通常显示为负数(如~5得-6);位运算在负数参与时结果符合补码规则,并非异常。

~ 运算符结果总是负数?不是“总是”,但几乎必然
PHP 中 ~ 对正整数取反后显示为负数,根本原因不是 PHP 故意“搞事情”,而是它用补码表示有符号整数,且 ~ 操作的是完整机器字长(通常是 64 位)的二进制位。比如 ~5 不是简单变成 2 或 0b1010,而是把 64 位全翻转:000…00101 → 111…11010。最高位(符号位)变成 1,CPU 和 PHP 就按补码规则解释为负数——所以输出 -6。
这不是 bug,是所有主流语言(C、Java、Python 等)在有符号整数上的统一行为。你看到的负数,其实是补码解码后的十进制等价值。
&、|、^ 在负数参与时为什么结果“看起来不对”
当你写 $a = -1; $b = 1; echo $a & $b;,结果是 1,而不是直觉中的 “0” 或报错。这是因为:
-
-1在 64 位补码中是全 1:即0xFFFFFFFFFFFFFFFF -
1是0x0000000000000001 - 逐位与之后,只有最低位两个都是 1,其余全是 0 → 结果就是
1
同理,-1 | 5 会得到 -1(因为全 1 | 任意数 = 全 1),-1 ^ 5 相当于对 5 的所有高位补 1 后再异或,结果是 -6。这些都不是异常,而是补码下位运算的自然结果。
想避免负数干扰?关键在类型和掩码
如果你本意是做无符号逻辑(比如处理网络协议字段、RGB 颜色值、权限位),别依赖默认整数输出。要主动截断或转换:
- 用
& 0xFF取低 8 位(如~$x & 0xFF得到 0–255 范围内的值) - 用
bindec(str_pad(decbin($x & 0xFF), 8, '0', STR_PAD_LEFT))强制按 8 位无符号解释(虽然通常没必要) - 对已知范围的值,用
if ($x 手动转为无符号等价(仅限 64 位环境且需确认 PHP_INT_SIZE) - 更稳妥:用
sprintf('%016x', $x)看十六进制,比十进制更反映真实位模式
位移运算(>)和负数一起用最危险
>> 是**算术右移**:负数右移时左边补符号位(1),所以 -4 >> 1 是 -2,但 -1 >> 1 还是 -1(全 1 右移一位还是全 1)。而 对负数左移可能直接溢出符号位,结果不可预测。
真正容易踩坑的是:你以为在做“乘 2”或“除 2”,却忘了输入可能是负数。例如循环中用 $flag 构造掩码,一旦 <code>$flag 变成负数,后续所有 & 判断都会失准——因为符号位参与了运算,不再是纯位标志。
所以,除非你明确需要符号扩展语义,否则位移前先确保操作数是非负的,或者改用 abs() + 显式掩码控制范围。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











