在 PHP 中处理 64 位无符号整数的正确方法是什么



当在64位机器上运行PHP时,最大整数值为0x7fffffffffffffff;9223372036854775807不同(除了在PHP 7之前的Windows上,它总是32位(。

根据相关的PHP手册页

PHP 不支持无符号整数。

当想要将使用uint64_t值的 C 源代码移植到 PHP 时,这是一个问题。特别是在查看严重依赖位移、旋转等的加密代码时。

处理 64 位无符号整数 (uint64( 值的正确(阅读:推荐(方法是什么,同时避免需要回退到 GMP 或 bcmath 等库来解决问题?

除了使用上述扩展之外,没有推荐的方法来处理无符号整数。

您可以尝试以下代码:

/// portably build 64bit id from 32bit hi and lo parts
function _Make64 ( $hi, $lo )
{
// on x64, we can just use int
if ( ((int)4294967296)!=0 )
    return (((int)$hi)<<32) + ((int)$lo);
// workaround signed/unsigned braindamage on x32
$hi = sprintf ( "%u", $hi );
$lo = sprintf ( "%u", $lo );
// use GMP or bcmath if possible
if ( function_exists("gmp_mul") )
    return gmp_strval ( gmp_add ( gmp_mul ( $hi, "4294967296" ), $lo ) );
if ( function_exists("bcmul") )
    return bcadd ( bcmul ( $hi, "4294967296" ), $lo );
// compute everything manually
$a = substr ( $hi, 0, -5 );
$b = substr ( $hi, -5 );
$ac = $a*42949; // hope that float precision is enough
$bd = $b*67296;
$adbc = $a*67296+$b*42949;
$r4 = substr ( $bd, -5 ) +  + substr ( $lo, -5 );
$r3 = substr ( $bd, 0, -5 ) + substr ( $adbc, -5 ) + substr ( $lo, 0, -5 );
$r2 = substr ( $adbc, 0, -5 ) + substr ( $ac, -5 );
$r1 = substr ( $ac, 0, -5 );
while ( $r4>100000 ) { $r4-=100000; $r3++; }
while ( $r3>100000 ) { $r3-=100000; $r2++; }
while ( $r2>100000 ) { $r2-=100000; $r1++; }
$r = sprintf ( "%d%05d%05d%05d", $r1, $r2, $r3, $r4 );
$l = strlen($r);
$i = 0;
while ( $r[$i]=="0" && $i<$l-1 )
    $i++;
return substr ( $r, $i );
}
list(,$a) = unpack ( "N", "xffxffxffxff" );
list(,$b) = unpack ( "N", "xffxffxffxff" );
$q = _Make64($a,$b);
var_dump($q);

在这里找到: https://www.percona.com/blog/2007/03/27/integers-in-php-running-with-scissors-and-portability/

在 PHP(64 位(中处理 64 位无符号整数的推荐方法是确保 PHP (result(整数 (msb( 的符号位不会因使用 PHP 运算符而溢出。

如果不溢出,生成的(有符号(PHP 整数将表示/模仿无符号整数类型 (uint64_t(。

密码学中常见的运算符是:算术(+,-,*和%(和按位(^,>>和<<(。

为了演示如何使用无符号整数操作数处理这些 PHP 运算符,我编写了一个 Uint64 类。它还包括强制转换函数(到/从字符串(。

链接: https://www.siderea.nl/php/class.uint64.txt

<?
class Uint64
{
    /*
        PHP class enables handling of the unsigned (64-bit) integer type
        version: 1.2
        Requires 64-bit build of PHP
        Functions using Arithmetic Operators:
        addition +  -> Add64($a,$b) -> returns ($a + $b)
        subtract -  -> Sub64($a,$b) -> returns ($a - $b)
        multiply *  -> Mul64($a,$b) -> returns ($a * $b)
        modulus  %  -> Mod64($a,$d) -> returns ($a % $d)
        Returned integer mimics the 64-bit unsigned integer type
        Bitwise operators in PHP will do fine except for the bitwise right shift ($x >> $bits)
        For correct usage of bitwise right shifts in PHP do -> ($x >> $bits) & (PHP_INT_MAX >> ($bits - 1))
        Casting functions.
        str2int()   raw/binary (8 byte) string to integer
        int2str()   integer to raw/binary (8 byte) string
        hex2int()   16 character hex string to integer
        int2dec()   integer to 20 character unsigned decimal string
        int2hex()   integer to 16 character hex string
        NOT yet supported:
        - division operator (/)
        New functions soon to be added:
        - Add64carry  -> returns sum and carry (carry as a third function argument by reference)
        - Mul64.128   -> returning 128 bit unsigned integer as 2 element array (64 upper bits, 64 lower bits)
    */
    public function str2int($str)
    {
        $split = unpack('N2', $str);
        return ($split[1] << 32) | $split[2];
    }
    public function int2str($int)
    {
        return hex2bin(sprintf('%016x',$int));
    }
    public function hex2int($str)
    {
        $split = unpack('N2', hex2bin($str));
        return ($split[1] << 32) | $split[2];
    }
    public function int2dec($int)
    {
        return sprintf('%020u',$int);
    }
    public function int2hex($int)
    {
        return sprintf('%016x',$int);
    }
    public function Mod64($a, $d)
    {
        if ($a < 0)
        {
            $mod = (($a & PHP_INT_MAX) % $d) + (PHP_INT_MAX % $d) + 1;
            if ($mod < $d)
            {
                return $mod;
            }
            return $mod - $d;
        }
        return $a % $d;
    }
    public function Mul64($a, $b)
    {
        $min    = ~PHP_INT_MAX;
        //$min  = PHP_INT_MIN;
        $mask60 = 0x000000000000000f;
        $mask34 = 0x000000003fffffff;
        $aL = $a & $mask60;
        $bH = ($b >> 60) & $mask60;
        $bL = $b & $mask60;
        $aH = ($a >> 60) & $mask60;
        $La = $a & $mask34;
        $Ha = ($a >> 30) & $mask34;
        $Lb = $b & $mask34;
        $Hb = ($b >> 30) & $mask34;
        $sum1 = (($La * $Hb) + ($Ha * $Lb)) << 30;
        $sum2 = ((($Ha * $Hb) + ($aL * $bH) + ($bL * $aH)) << 60) | ($La * $Lb);
        //
        $sum = $min + ($sum1 & PHP_INT_MAX) + ($sum2 & PHP_INT_MAX);
        if (($sum1 ^ $sum2) < 0)
        {}
        else
        {
            $sum ^= $min;
        }
        return $sum;
    }
    public function Add64($a, $b)
    {
        $min    = ~PHP_INT_MAX;
        //$min  = PHP_INT_MIN;
        $sum = $min + ($a & PHP_INT_MAX) + ($b & PHP_INT_MAX);
        if (($a ^ $b) < 0)
        {
            return $sum;
        }
        return $sum ^ $min;
    }
    public function Sub64($a, $b)
    {
        $min    = ~PHP_INT_MAX;
        //$min  = PHP_INT_MIN;
        if ($a < 0)
        {
            $a &= PHP_INT_MAX;
            if ($b < 0)
            {
                $b &= PHP_INT_MAX;
                if ($a < $b)
                {
                    return $min + (PHP_INT_MAX - $b) + $a + 1;
                }
                else
                {
                    return $a - $b;
                }
            }
            else
            {
                if ($a < $b)
                {
                    return ($min + (PHP_INT_MAX - $b) + $a + 1) ^ $min;
                }
                else
                {
                    return ($a - $b) ^ $min;
                }
            }
        }
        else
        {
            if ($b < 0)
            {
                $b &= PHP_INT_MAX;
                if ($a < $b)
                {
                    return ($min + (PHP_INT_MAX - $b) + $a + 1) ^ $min;
                }
                else
                {
                    return ($a - $b) ^ $min;
                }
            }
            else
            {
                if ($a < $b)
                {
                    return $min + (PHP_INT_MAX - $b) + $a + 1;
                }
                else
                {
                    return $a - $b;
                }
            }
        }
    }
}
?>

相关内容

最新更新