已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > PHP > 讨论/求助

标题: php如何实现无符号右移

作者: @Ta

时间: 2021-08-22

点击: 18132

php怎么实现无符号右移,类似js的>>>

[隐藏样式|查看源码]


『回复列表(25|隐藏机器人聊天)』

1.

@幕后导演,可以写函数实现,比如:

// 32位无符号右移
function unsignedRight32($a, $n) {
    $c = 2147483647 >> ($n - 1);
    return $c & ($a >> $n);
}

// 64位无符号右移
function unsignedRight64($a, $n) {
    if (PHP_INT_MAX == 2147483647) {
        throw new Exception('32位PHP不支持64位整数');
    }
    $c = PHP_INT_MAX >> ($n - 1);
    return $c & ($a >> $n);
}
(/@Ta/2021-08-22 07:57//)

2.

32位和64位负整数的无符号右移结果是不同的,因为一个符号位在第32位,一个符号位在第64位。右移出来的肯定是两个完全不同的正整数。

而PHP的32位版使用32位整数,64位版使用64位整数。如果它直接提供无符号右移运算符,32位版和64位版的结果就会不同,就会引起困惑。所以它没有提供。

自行实现的右移操作可以用不同的函数模拟32位和64位右移结果。


js中所有数值都是double类型,位运算时转为32位整数进行操作,所以结果是确定的,没有这种困扰。

java/c/c++中你可以自行指定整数位宽,所以也没有这种困扰。

(/@Ta/2021-08-22 07:52//)

3. @老虎会游泳,就是32位和64位结果不同,导致我服务器和本地计算出来的不一样,很是惆怅
(/@Ta/2021-08-22 09:40//)

4.

@幕后导演,在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
(/@Ta/2021-08-22 10:12//)

5.

@幕后导演,我发现直接用负数& 0xffffffff就能把它转成uint32,不知道是不是特例:

$a = -1 & 0xffffffff;
$b = 33333333;
$c = (($a >> 4) + (($b << 20) & 0xffffffff)) & 0xffffffff;
var_dump($a, $b, $c);

得到:

int(4294967295)
int(33333333)
int(357564415)
(/@Ta/2021-08-22 10:17//)

6.

减法也要& 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位无符号运算了。

(/@Ta/2021-08-22 10:37//)

7.

如果要把结果转成有符号整数,可以这样做:

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)
(/@Ta/2021-08-22 10:41//)

8. @老虎会游泳,已经解决啦,我服务器上是64位,在本地测试是32位,所以结果不一样,将字符串长度超出则截取底32位,长度不够,则填充高位为0到32位
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));
}
(/@Ta/2021-08-22 15:18//)

9.

@幕后导演思路巧妙

(/@Ta/2021-08-22 15:24//)

10. @老虎会游泳,那么问题来了,php64位使用>>和32位的结果又不一样了
35393633961922>>2
php 计算结果是8848408490480
js 计算结果是-297881104
当php打印1000>>8 = 3,js打印出来同样是3
(/@Ta/2021-08-22 17:15//)

11.

@幕后导演,35393633961922超出了int32的范围,所以它要先被转换为一个int32,然后再位移才行。

function sint32($int) {
    return unpack('l', pack("L", $int))[1];
}

var_dump(sint32(35393633961922) >> 2);

得到 int(-297881104)

(/@Ta/2021-08-22 17:28//)

12. @老虎会游泳
35393633961922 << 28 
在js打印结果是536870912
用上面你给的函数结果是-319847399407222784
(/@Ta/2021-08-22 17:43//)

13.

@幕后导演,结果超出了int32,你要对这个结果再次使用sint32。

function sint32($int) {
    return unpack('l', pack("L", $int))[1];
}

var_dump(sint32(sint32(35393633961922) << 28));
(/@Ta/2021-08-22 17:50//)

14.

@幕后导演,如果结果可能大于2147483647,或者小于-2147483648,就应该对其调用sint32

(/@Ta/2021-08-22 17:51//)

15. @老虎会游泳
那么问题又来了,对于php异或运算和js异或运算的结果出来不一样
35393633961922 ^ 3963386674
js 打印结果是1422310640
php 打印出来结果是35391952829680
很是迷惑
(/@Ta/2021-08-22 18:25//)

16.

@幕后导演

function sint32($int) {
    return unpack('l', pack("L", $int))[1];
}

var_dump(sint32(sint32(35393633961922) ^ sint32(3963386674)));

int(1422310640)

(/@Ta/2021-08-22 18:29//)

17.

@幕后导演,对了,32位无符号右移还可以写成这样:

function shr32($x, $bits) {
    if (PHP_INT_MAX == 0x7fffffff) {
        throw new Exception('32位PHP不支持64位整数');
    }
    return ($x & 0xffffffff) >> $bits;
}

var_dump(shr32(-1, 2));

int(1073741823),结果和js一致。

比你的简单很多。

(/@Ta/2021-08-23 08:08//)

18.

这么多年的php白写了,看不懂

(/@Ta/2021-08-22 23:24//)

19. @老虎会游泳,已经搞定了,在64位上能算准确,但是放到32位上的机器又算不准确了,所以搞了两份代码,感谢大佬
(/@Ta/2021-08-23 00:28//)

下一页 1/2页,共25楼

回复需要登录

8月18日 22:47 星期一

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1