Как читать / записывать 64-разрядные беззнаковые целые числа с начальным порядком?

#php

#php

Вопрос:

pack не поддерживает их, так как же читать и записывать 64-разрядные беззнаковые целые числа с начальным порядком?

Комментарии:

1. Это не помогло? ca2.php.net/manual/en/function.pack.php#109328

2. 64-разрядные целые числа без знака недоступны ни в одной сборке PHP AFAIK. Итак, вам нужно что-то вроде GMP.

3. @Jon 0xffffffffffffffffffffff выходит как float(1.844674407371E 19) вместо 18446744073709551615.

4. @Mark: Уверен, что это будет выглядеть как значение с плавающей точкой, но я не уверен, как это связано с моим комментарием.

5. Почему бы просто не разделить / объединить в два 32-разрядных целых числа без знака и прочитать / записать их в формате с начальным порядком двумя вызовами?

Ответ №1:

Вы могли бы рассматривать 64-разрядные числа как непрозрачный фрагмент из 8 байт двоичных данных и использовать обычные функции обработки строк для их обработки (т.Е. substr и, прежде всего, оператор dot).

Всякий раз, когда (и если) вам нужно выполнить над ними арифметические вычисления, вы можете использовать пару функций-оболочек для кодирования / декодирования этого фрагмента в его шестнадцатеричном представлении (в моей реализации я называю этот промежуточный тип hex64 ) и использовать внешнюю библиотеку, которую вы предпочитаете для выполнения реальной работы:

 <?php

/* Save as test.php */

function chunk_to_hex64($chunk) {
    assert(strlen($chunk) == 8);
    return strrev(bin2hex($chunk));
}

function hex64_to_chunk($hex64) {
    assert(strlen($hex64) == 16);
    return strrev(pack('h*', $hex64));
}


// Test code:

function to_hex64($number) {
    $hex64 = base_convert($number, 10, 16);
    // Ensure the hex64 is left padded with '0'
    return str_pad($hex64, 16, '0');
}

for ($number = 0.0; $number < 18446744073709551615.0; $number  = 2932031007403.0) {
    $hex64 = to_hex64($number);
    $hex64_reencoded = chunk_to_hex64(hex64_to_chunk($hex64));
    assert($hex64_reencoded == $hex64, "Result is $hex64_reencoded, expected $hex64");
}

$data = file_get_contents('test.php');
// Skip the last element because it is not 8 bytes
$chunks = array_slice(str_split($data, 8), 0, -1);
foreach ($chunks as $chunk) {
    $hex64 = to_hex64($number);
    $chunk_reencoded = hex64_to_chunk(chunk_to_hex64($chunk));
    assert($chunk_reencoded == $chunk, "Result is $chunk_reencoded, expected $chunk");
}
  

Комментарии:

1. 0xffffffffffffffffff — это> PHP_INT_MAX, даже на 64-разрядных машинах. Это было сделано намеренно?

2. Это часть тестирования, так что это не имеет значения: у меня нет под рукой платформы 32. В любом случае, я изменил его на использование чисел с плавающей запятой, поэтому я думаю, что он может работать на 32-разрядных платформах без проблем.

3. 0xFFFFFFFFFFFFFFFF уже достается автоматически на поплавок. На самом деле цикл даже не используется $number , поэтому я не уверен, что вы тестируете.

4. Цикл использует $number , но моя последняя правка испортила форматирование. Я только что добавил недостающий символ новой строки.

Ответ №2:

Я написал вспомогательный класс для упаковки / распаковки 64-разрядных целых чисел без знака.

Соответствующий бит — это всего две строки:

 $ints = unpack("@$offset/Vlo/Vhi", $data);
$sum = Math::add($ints['lo'], Math::mul($ints['hi'], '4294967296'));
  

( Math::* это простая оболочка для bcmath)

И упаковка:

 $out .= pack('VV', (int)bcmod($args[$idx],'4294967296'), (int)bcdiv($args[$idx],'4294967296'));
  

Он разбивает 64-разрядные целые числа на два 32-разрядных целых числа, которые 64-разрядный PHP должен поддерживать 🙂