PHP中如何翻转位且不产生前置符号?
解决PHP中~位取反后二进制输出异常及数值不匹配问题
我来帮你理清这个问题的根源和解决办法——这个坑其实是PHP有符号整数的特性导致的,很多刚接触位运算的开发者都会碰到。
先看你给出的示例代码,整理后是这样的:
$integer = 0b11110; // 十进制是30 echo "{$integer}: " . decbin($integer) . PHP_EOL; // 输出:30: 11110 $integer = ~$integer; // 执行位取反 $binary = decbin($integer); echo "{$integer}: " . $binary . PHP_EOL; // 输出类似:-31: 11111111111111111111111111100001
为什么会出现大量前置符号/数值不相等?
- PHP整数是有符号的:PHP使用的是基于系统字长的有符号整数(通常是64位或32位),
~运算符会对**每一位(包括最高位的符号位)**进行取反操作。正数的符号位是0,取反后变成1,直接把数值变成了负数。 - decbin的无符号处理:
decbin()函数在处理负数时,会把它当作无符号大整数来解析,所以会把补码形式的所有高位1都输出出来,这就是你看到的一堆前置1的原因。 - 数值不匹配的本质:如果你把
decbin()输出的二进制字符串用bindec()转回整数,得到的会是一个超大的无符号整数(比如32位系统下是4294967265),和取反后的负数(-31)当然不是同一个值——一个是无符号的大整数,一个是有符号的负数,存储和解析逻辑完全不同。
解决办法
根据你的需求,分两种情况处理:
1. 只想对指定位数的二进制位取反(忽略符号位)
如果你的原始值是固定位数的二进制(比如示例里的5位11110),可以用掩码来限制取反的范围,只保留我们关心的低位:
$integer = 0b11110; // 原始值30 $targetBitLength = 5; // 我们只操作这5位 echo "原始值: {$integer} -> 二进制: " . decbin($integer) . PHP_EOL; // 生成对应位数的全1掩码:(1 << 5) - 1 = 0b11111 $mask = (1 << $targetBitLength) - 1; // 先取反,再用掩码过滤掉高位的符号位影响 $inverted = ~$integer & $mask; echo "取反后(仅5位): {$inverted} -> 二进制: " . decbin($inverted) . PHP_EOL;
输出结果:
原始值: 30 -> 二进制: 11110 取反后(仅5位): 1 -> 二进制: 1
2. 正确处理有符号整数的取反与二进制表示
如果你需要保留有符号整数的特性,同时想得到正确的二进制补码表示,可以用sprintf格式化输出指定字长的二进制:
$integer = 0b11110; $integer = ~$integer; // 输出32位的二进制补码表示 echo "有符号值: {$integer} -> 32位补码: " . sprintf('%032b', $integer) . PHP_EOL; // 输出64位的二进制补码表示 echo "有符号值: {$integer} -> 64位补码: " . sprintf('%064b', $integer) . PHP_EOL;
这样就能看到符合有符号整数补码规则的二进制字符串,而不是一堆无意义的前置1。
内容的提问来源于stack exchange,提问作者user5741640
相关产品推荐
相关产品推荐

