32位和64位负整数的无符号右移结果是不同的,因为一个符号位在第32位,一个符号位在第64位。右移出来的肯定是两个完全不同的正整数。
而PHP的32位版使用32位整数,64位版使用64位整数。如果它直接提供无符号右移运算符,32位版和64位版的结果就会不同,就会引起困惑。所以它没有提供。
自行实现的右移操作可以用不同的函数模拟32位和64位右移结果。
js中所有数值都是double类型,位运算时转为32位整数进行操作,所以结果是确定的,没有这种困扰。
java/c/c++中你可以自行指定整数位宽,所以也没有这种困扰。
@幕后导演,在64位PHP中,使用pack和unpack函数就能把负数数值转换成32位无符号数值:
function uint32($int) {
if (PHP_INT_MAX == 0x7fffffff) {
throw new Exception('32位PHP不支持64位整数');
}
return unpack('P', pack("VV", $int, 0))[1] & 0xffffffff;
}
var_dump(uint32(-1));
然后你就可以使用普通位移运算符进行运算了,左移、加、乘时都加上 & 0xffffffff
就可以保证结果始终在32位整数范围内。
$a = uint32(-1);
$b = uint32(33333333);
$c = (($a >> 4) + (($b << 20) & 0xffffffff)) & 0xffffffff;
var_dump($a, $b, $c);
得到:
int(4294967295)
int(33333333)
int(357564415)
和js的结果一致:
console.log((-1 >>> 4) + (33333333 << 20));
// 357564415
减法也要& 0xffffffff
来保证结果为32位无符号整数,因为结果有可能为负
var_dump(1 - 2);
var_dump((1 - 2) & 0xffffffff);
得到
int(-1)
int(4294967295)
除法进行& 0xffffffff
可以消除小数点:
var_dump(4 / 3);
var_dump((4 / 3) & 0xffffffff);
得到:
float(1.3333333333333)
int(1)
所以,对所有操作数和每一步运算结果都进行& 0xffffffff
,就可以在64位PHP里模拟32位无符号运算了。
如果要把结果转成有符号整数,可以这样做:
function sint32($int) {
if (PHP_INT_MAX == 0x7fffffff) {
throw new Exception('32位PHP不支持64位整数');
}
return unpack('l', pack("L", $int))[1];
}
var_dump(sint32(0xffff0000));
得到:
int(-65536)
function shr32($x, $bits) {
if ($bits >= 32) {
return 0;
}
// 转换成代表二进制数字的字符串
$bin = decbin($x);
$l = strlen($bin);
// 字符串长度超出则截取底32位,长度不够,则填充高位为0到32位
if ($l > 32) {
$bin = substr($bin, $l - 32, 32);
} elseif ($l < 32) {
$bin = str_pad($bin, 32, '0', STR_PAD_LEFT);
}
// 取出要移动的位数,并在左边填充0
return bindec(str_pad(substr($bin, 0, 32 - $bits), 32, '0', STR_PAD_LEFT));
}
这么多年的php白写了,看不懂
@幕后导演,可以写函数实现,比如: