При запуске PHP на 64-битных машинах максимальное целое значение равно 0x7fffffffffffffff
; по-разному 9223372036854775807
(кроме Windows до PHP 7, где он всегда был 32-битным).
Согласно соответствующей странице справки PHP
PHP не поддерживает целые числа без знака.
Это небольшая проблема, когда вы хотите портировать исходный код C, который использует uint64_t
значения, в 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", "\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/
Рекомендуемый способ обработки 64-разрядных целых чисел без знака в PHP (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;
}
}
}
}
}
?>