简体   繁体   English

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

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

I'm trying to write an algorithm to convert a 64Bit integer to 32Bit via truncation, and return a value accurately representing a 32Bit value. 我正在尝试编写一种算法,以通过截断将64Bit整数转换为32Bit,并返回准确表示32Bit值的值。 The obvious problem is that integers in PHP are only 64Bit (barring 32Bit systems). 明显的问题是PHP中的整数只有64Bit(禁止32Bit系统)。

I first tried the following: 我首先尝试了以下方法:

$x = $v & 0xFFFFFFFF

So if $v = PHP_INT_MAX , $x is 4294967295 , when really I want -1 . 因此,如果$v = PHP_INT_MAX ,则$x4294967295 ,当我真正想要-1 I know the reason for this is that when represented as a 64Bit integer, PHP prepends the zeros to the last 32 bits, and thats why I get a positive number. 我知道这样做的原因是,当用64Bit整数表示时,PHP将零添加到最后32位,这就是为什么我得到一个正数。

My solution so far is this: 到目前为止,我的解决方案是:

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

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

    return $v;
}

And I'm pretty sure its right. 而且我很确定它是正确的。 The problem I have with this is that it requires that if statement to inspect whether the truncated number is negative. 我的问题是,它要求使用if语句检查截断后的数字是否为负。 I was really hoping for a bitwise only solution. 我真的希望有一个按位解决方案。 It may not be possible, but I thought I'd ask. 可能不可能,但是我想问一下。

EDIT: 编辑:

The problem can be simplified to just part of the solution. 可以将问题简化为解决方案的一部分。 ie if the first bit is 1 , make all bits 1 , if first bit is 0 make all bits 0 . 即,如果第一位为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

I would be able to use the LSBs to derive this value, then mask it onto the MSBs of the 64Bit integer. 我将能够使用LSB导出此值,然后将其屏蔽到64Bit整数的MSB上。 This is what I'm trying to figure out now, though I'm trying to avoid creating a monster equation. 这就是我现在想要弄清楚的,尽管我试图避免创建怪物方程。

There's probably a more elegant/efficient way using bitwise operations, but you can also force the conversion with pack() and unpack() . 使用按位运算可能是一种更优雅/有效的方法,但是您也可以使用pack()unpack()强制转换。 Eg: Pack as unsigned, unpack as signed. 例如:打包为未签名,解压缩为已签名。

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

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

I'm curious what you're applying this to, because I had to break a hash function with this same thing some years ago when I moved an app from a 32 bit machine to a 64 bit machine, but I can't remember what it was. 我很好奇您将此应用到什么应用程序,因为几年前当我将应用程序从32位计算机迁移到64位计算机时,我不得不用同样的方法破坏哈希函数,但是我不记得是什么它是。

Edit: Wow for some reason I remembered Two's Complement being hard. 编辑:哇,由于某种原因,我记得Two's Complement很辛苦。 Literally just invert and add one. 从字面上看,只需反转并添加一个即可。

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

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

Edit 2: I saw that you want to extend this to arbitrary bit lengths, in which case you just need to calculate the masks instead of explicitly setting them: 编辑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)
);

I left in the debug var_dump() s for output flavor: 我留在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)

I think I've cracked it: 我想我已经破解了:

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

The solution was actually staring me in the face. 解决方案实际上是盯着我。 Get the value of the first bit, then multiply it by the max value of unsigned 32bit integer. 获取第一位的值,然后将其乘以无符号32位整数的最大值。

If someone can come up with a better or shorter solution, I'll also accept that. 如果有人可以提出更好或更短的解决方案,我也会接受。

PS. PS。 This is only for 32Bit, i also intend to use this proof for 16Bit and 8Bit. 这仅适用于32Bit,我也打算将此证明用于16Bit和8Bit。


Expanded, $x is input, $z is output. 展开后,输入$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