繁体   English   中英

将64位整数截断为32位并模拟值

[英]Truncate 64Bit Integer to 32Bit and simulate value

我正在尝试编写一种算法,以通过截断将64Bit整数转换为32Bit,并返回准确表示32Bit值的值。 明显的问题是PHP中的整数只有64Bit(禁止32Bit系统)。

我首先尝试了以下方法:

$x = $v & 0xFFFFFFFF

因此,如果$v = PHP_INT_MAX ,则$x4294967295 ,当我真正想要-1 我知道这样做的原因是,当用64Bit整数表示时,PHP将零添加到最后32位,这就是为什么我得到一个正数。

到目前为止,我的解决方案是:

function convert64BitTo32Bit(int $v): int
{
    $v &= 0xFFFFFFFF;

    if ($v & 0x80000000) {
        return $v | 0xFFFFFFFF00000000;
    }

    return $v;
}

而且我很确定它是正确的。 我的问题是,它要求使用if语句检查截断后的数字是否为负。 我真的希望有一个按位解决方案。 可能不可能,但是我想问一下。

编辑:

可以将问题简化为解决方案的一部分。 即,如果第一位为1 ,则将所有位设为1 ;如果第一位为0 ,则将所有位设为0

# example assumes input is the LSBs of an 8Bit integer.
# scale for 64Bit and the same solution should work.

op(1010) = 1111
op(0101) = 0000

op(0000) = 0000
op(1000) = 1111
op(0001) = 0000

我将能够使用LSB导出此值,然后将其屏蔽到64Bit整数的MSB上。 这就是我现在想要弄清楚的,尽管我试图避免创建怪物方程。

使用按位运算可能是一种更优雅/有效的方法,但是您也可以使用pack()unpack()强制转换。 例如:打包为未签名,解压缩为已签名。

function overflow32($in) {
    return unpack('l', pack('i', $in & 0xFFFFFFFF))[1];
}

var_dump( overflow32(pow(2,33)-1) ); // int(-1)

我很好奇您将此应用到什么应用程序,因为几年前当我将应用程序从32位计算机迁移到64位计算机时,我不得不用同样的方法破坏哈希函数,但是我不记得是什么它是。

编辑:哇,由于某种原因,我记得Two's Complement很辛苦。 从字面上看,只需反转并添加一个即可。

function overflow32($in) {
    if( $in & 0x80000000 ) {
        return ( ( ~$in & 0xFFFFFFFF) + 1 ) * -1;
    } else {
        return $in & 0xFFFFFFFF;
    }
}

var_dump( kludge32(pow(2,33)-1) );

编辑2:我看到您想将此扩展为任意位长度,在这种情况下,您只需要计算掩码即可,而不是显式设置掩码:

function overflow_bits($in, $bits=32) {
    $sign_mask = 1 << $bits-1;
    $clamp_mask = ($sign_mask << 1) - 1;

    var_dump(
        decbin($in),
        decbin($sign_mask),
        decbin($clamp_mask)
    );

    if( $in & $sign_mask ) {
        return ( ( ~$in & $clamp_mask) + 1 ) * -1;
    } else {
        return $in & $clamp_mask;
    }
}

var_dump(
    overflow_bits(pow(2, 31), 32),
    overflow_bits(pow(2, 15), 16),
    overflow_bits(pow(2, 7), 8)
);

我留在debug var_dump()以获取输出风格:

string(32) "10000000000000000000000000000000"
string(32) "10000000000000000000000000000000"
string(32) "11111111111111111111111111111111"
string(16) "1000000000000000"
string(16) "1000000000000000"
string(16) "1111111111111111"
string(8) "10000000"
string(8) "10000000"
string(8) "11111111"
int(-2147483648)
int(-32768)
int(-128)

我想我已经破解了:

function overflow32Bit(int $x): int
{
    return ($x & 0xFFFFFFFF) | ((($x & 0xFFFFFFFF) >> 31) * ((2 ** 32) - 1) << 32);
}

var_dump(overflow32Bit(PHP_INT_MAX));     // -1          correct
var_dump(overflow32Bit(PHP_INT_MAX - 1)); // -2          correct
var_dump(overflow32Bit(PHP_INT_MIN));     //  0          correct
var_dump(overflow32Bit((2 ** 31) - 1));   //  2147483647 correct
var_dump(overflow32Bit((2 ** 31)));       // -2147483647 correct
var_dump(overflow32Bit(0xFFFFFFFF));      // -1          correct
var_dump(overflow32Bit(0x7FFFFFFF));      //  2147483647 correct

解决方案实际上是盯着我。 获取第一位的值,然后将其乘以无符号32位整数的最大值。

如果有人可以提出更好或更短的解决方案,我也会接受。

PS。 这仅适用于32Bit,我也打算将此证明用于16Bit和8Bit。


展开后,输入$x ,输出$z

$x = PHP_INT_MAX;
$y = (2 ** 32) - 1;
$z = ($x & $y) | ((($x & $y) >> 31) * ($y << 32));

var_dump($z);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM