【问题标题】:What is the correct way to handle 64 bit unsigned integers in PHP?在 PHP 中处理 64 位无符号整数的正确方法是什么?
【发布时间】:2018-04-09 19:38:53
【问题描述】:

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

According to the related PHP manual page

PHP 不支持无符号整数。

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

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

【问题讨论】:

    标签: php uint64


    【解决方案1】:

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

    你可以试试这个代码:

    /// 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", "\xff\xff\xff\xff" );
    list(,$b) = unpack ( "N", "\xff\xff\xff\xff" );
    $q = _Make64($a,$b);
    var_dump($q);
    

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

    【讨论】:

    • 为了运维,我真的希望他正在转换的这个项目很小。
    【解决方案2】:

    在 PHP(64 位)中处理 64 位无符号整数的推荐方法是确保 PHP(结果)整数 (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;
                    }
                }
            }
        }
    }
    
    ?>
    

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • 把完整的代码贴在这里合适吗?
    猜你喜欢
    • 2016-06-28
    • 1970-01-01
    • 2017-11-07
    • 1970-01-01
    • 2012-02-11
    • 1970-01-01
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多