You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Функция gmp_export() должна выдавать в качестве результата бинарную строку длина которой указывается во втором параметре. Но при преобразовании нуля "0" функция возвращает в качестве результата пустую строку длиной 0 байт независимо от значения второго параметра. Такое поведение функции недопустимо так как приводит к фактической потере данных при преобразовании и делает невозможным их восстановление.
Применяя функцию gmp_export() для упаковки числа в бинарный формат программист надеется на адекватное воспроизведение исходного числа из бинарной строки при использовании функции gmp_import(), но в данном случае эта логика нарушается.
Программисты «благодаря» этой несуразности, должны писать в своих программах костыли для парирования этой ситуации.
Прошу привести работу функции к естественному порядку.
The gmp_export() function should return a binary string whose length is specified in the second parameter. However, when converting zero "0," the function returns an empty string of 0 bytes as a result, regardless of the value of the second parameter. This behavior is unacceptable, as it leads to actual data loss during conversion and makes it impossible to recover.
When using the gmp_export() function to pack a number into binary format, the programmer hopes for an adequate reproduction of the original number from the binary string when using the gmp_import() function, but in this case, this logic is violated.
Due to this inconsistency, programmers are forced to write workarounds in their programs to handle this situation.
I request that the function's operation be brought back to its natural order.
If op is non-zero then the most significant word produced will be non-zero. If op is zero then the count returned will be zero and nothing written to rop. If rop is NULL in this case, no block is allocated, just NULL is returned.
If op is zero then the count returned will be zero and nothing written to rop
Yes, the function does that, but it shouldn't be like that! This leads to data loss - they are simply not being forwarded! If there is a 0 in the message, the recipient will not know about it. This is the claim.
And it is a claim that should be directed upstream to the GMP maintainers. Having PHP's GMP export fudged to do something different from everyone else's would lead to incompatibilities.
Incidentally, where is the data loss? If you export a zero, you get a zero-length string; import a zero-length string and you get a zero. Nothing lost.
As @MorganLOCode said, I don't see how there is data loss: https://3v4l.org/ZCBqp and we expose the GMP behaviour. So this is going to need to clarify why you consider there to be some sort of data loss.
Reacted by Ilija Tovilo, Arnaud Le Blanc and David CARLIER
A simple example illustrates the situation.
We have a sequence of numbers: 1, 2, 0, 7, 11, 0, 23, 55
We convert each number into a 4-byte binary string using gmp_export(a, 4) and combine them into a single string.
We obtain a final string of 24 bytes (4 * 6) instead of 32 bytes, as the two 0's are converted to empty strings.
We send the final string to the client, which unpacks it using gmp_import(a, 4) and obtains the following sequence: 1, 2, 7, 11, 23, 55
This is how data loss occurs.
Original text
Простой пример демонстрирующий ситуацию.
Имеется числовая последовательность: 1, 2, 0, 7, 11, 0, 23, 55
Преобразуем каждое число в бинарную строку длиной 4 байта gmp_export(a, 4) и собираем их в общую итоговую строку.
Получаем итоговую строку длиной 24 байта (4*6) вместо 32 байт так два числа 0 преобразовываются в пустые строки.
Пересылаем итоговую строку клиенту, он распаковывает её при помощи функции gmp_import(a, 4) и получает следующую последовательность: 1, 2, 7, 11, 23, 55
Вот так происходит потеря данных.
This problem is not specific to PHP, and I'm not sure what guarantees GMP provides in terms of its serialization format, i.e. it's probably not safe to just concatenate the values. FWIU, there is no value other than "" that deserializes to 0. So, there's at the very least nothing we can do. Please raise this problem on the GMP bug tracker.
The problem looks like it's with how the numbers are being concatenated together; exported GMP numbers vary in length depending on how many digits they require. Here, the word size is being used as padding (rather than controlling word/byte ordering) on the assumption that every number requires no more than four bytes (and does require at least one). I suspect GMP isn't being used at all except for this particular step.
Seems like this use case would be better served by using pack():
@iluuu1994; any leading NUL bytes get stripped on deserialisation (they're basically leading zero digits), so a string consisting entirely of NULs would deserialise to 0. It's definitely not safe to just concatenate exported GMP numerals since they're variable-length and not self-delimiting.
Я решал задачу передачи массива 32 разрядных чисел от сервера клиенту из PHP в JS и и обратно из JS в PHP. Каждое число из массива преобразовывается отдельно в бинарную строку длиной 4 байта, все полученный бинарные строки складываются в одну итоговую строку и эта строка пересылается клиенту. У клиента эта строка трактуется как бинарный массив Uint32Array(binStr).
В этой схеме логично использовать зеркальные функции gmp_export() и gmp_import().
Относительно замечания @iluuu1994 "It's definitely not safe to just concatenate exported GMP numerals since they're variable-length and not self-delimiting" оно ошибочно. Бинарные строки получаемые в результате GMP упаковки имеют постоянную длину и эта длина известна она декларируется в параметре функции $word_size и позволяет разделять упакованную строку на отдельные числа при распаковке.
Вообще описание функции GMP_EXPORT в документации это сборник загадок.
gmp_export(GMP|int|string $num, int $word_size = 1, int $flags = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN): string
Параметры функции GMP num - GMP-число для экспорта.
Загадка #1. Про int и string нет ни каких пояснений, что они из себя представляют. Можно конечно строить догадки, но это плохой стиль для документации.
word_size- По умолчанию равно 1. Количество байт в каждомблоке бинарных данных. Обычно используется совместно с заданием параметра options.
Загадка #2. Каждый блок бинарных данных - а что такое блок? а их несколько? а сколько? а что в каждом их них содержится?
Загадка #3. Параметр options вообще нигде ни до ни после этого места не фигурирует, думай что хочешь.
Загадка #4. Ни каких ограничений на значение word_size не приводится, хотя очевидно что это не так, оно не может быть произвольным числом.
Are you suggesting this is more a documentation bug? There is a user note from five years ago giving examples of the various flags' effects that could perhaps be folded into the main page. I agree that the constants should be better described, since they aren't the same as the 1/-1/0 values used by GMP's mpz_export.
PHP Constant
mpz_export
GMP_MSW_FIRST
order=1
GMP_LSW_FIRST
order=-1
GMP_LITTLE_ENDIAN
endian=-1
GMP_BIG_ENDIAN
endian=1
GMP_NATIVE_ENDIAN
endian=0
The binary strings obtained as a result of GMP packages are constant and this length is known to be declared in the match of the function $word_size and allows you to divide the packed string into separate numbers when unpacking.
Not really; 0 serialises to a string of length zero, and numbers greater than 232-1 serialise to strings that are more than four bytes long (so, longer than one word).
The word size and option flags control the order in which bytes appear within the string (see the user note mentioned above), not the string length.
I solved the problem of transferring an array of 32 bit numbers from the client to the client from PHP to JS and back from JS to PHP. Each number from the array is converted separately into a binary row with a length of 4 bytes, all obtained binary lines are folded into one final row and this line is sent to the client. In the client, this line is interpreted as a binary array of Uint32Array (binStri).
In this scheme, it is logical to use mirror functions gmp_export() and gmp_import().
Since you're not using multiprecision arithmetic, it would seem more logical to use the already-mentioned pack() (and unpack() if you're deserialising in PHP as well). http://www.php.net/pack http://www.php.net/unpack
Activity
[-]Потеря данных при преобразования чисел[/-][+]Data loss when converting numbers[/+]iluuu1994 commentedon Oct 27, 2025
/cc @Girgias
MorganLOCode commentedon Oct 28, 2025
Note that this is also upstream behaviour.
https://gmplib.org/manual/Integer-Import-and-Export
aub2000 commentedon Oct 28, 2025
Yes, the function does that, but it shouldn't be like that! This leads to data loss - they are simply not being forwarded! If there is a 0 in the message, the recipient will not know about it. This is the claim.
MorganLOCode commentedon Oct 29, 2025
And it is a claim that should be directed upstream to the GMP maintainers. Having PHP's GMP export fudged to do something different from everyone else's would lead to incompatibilities.
Incidentally, where is the data loss? If you export a zero, you get a zero-length string; import a zero-length string and you get a zero. Nothing lost.
Girgias commentedon Oct 29, 2025
As @MorganLOCode said, I don't see how there is data loss: https://3v4l.org/ZCBqp and we expose the GMP behaviour. So this is going to need to clarify why you consider there to be some sort of data loss.
aub2000 commentedon Nov 1, 2025
A simple example illustrates the situation.
We have a sequence of numbers: 1, 2, 0, 7, 11, 0, 23, 55
We convert each number into a 4-byte binary string using gmp_export(a, 4) and combine them into a single string.
We obtain a final string of 24 bytes (4 * 6) instead of 32 bytes, as the two 0's are converted to empty strings.
We send the final string to the client, which unpacks it using gmp_import(a, 4) and obtains the following sequence: 1, 2, 7, 11, 23, 55
This is how data loss occurs.
Original text
Простой пример демонстрирующий ситуацию.
Имеется числовая последовательность: 1, 2, 0, 7, 11, 0, 23, 55
Преобразуем каждое число в бинарную строку длиной 4 байта gmp_export(a, 4) и собираем их в общую итоговую строку.
Получаем итоговую строку длиной 24 байта (4*6) вместо 32 байт так два числа 0 преобразовываются в пустые строки.
Пересылаем итоговую строку клиенту, он распаковывает её при помощи функции gmp_import(a, 4) и получает следующую последовательность: 1, 2, 7, 11, 23, 55
Вот так происходит потеря данных.
iluuu1994 commentedon Nov 3, 2025
This problem is not specific to PHP, and I'm not sure what guarantees GMP provides in terms of its serialization format, i.e. it's probably not safe to just concatenate the values. FWIU, there is no value other than
""that deserializes to0. So, there's at the very least nothing we can do. Please raise this problem on the GMP bug tracker.MorganLOCode commentedon Nov 3, 2025
The problem looks like it's with how the numbers are being concatenated together; exported GMP numbers vary in length depending on how many digits they require. Here, the word size is being used as padding (rather than controlling word/byte ordering) on the assumption that every number requires no more than four bytes (and does require at least one). I suspect GMP isn't being used at all except for this particular step.
Seems like this use case would be better served by using pack():
@iluuu1994; any leading NUL bytes get stripped on deserialisation (they're basically leading zero digits), so a string consisting entirely of NULs would deserialise to 0. It's definitely not safe to just concatenate exported GMP numerals since they're variable-length and not self-delimiting.
aub2000 commentedon Nov 4, 2025
Я решал задачу передачи массива 32 разрядных чисел от сервера клиенту из PHP в JS и и обратно из JS в PHP. Каждое число из массива преобразовывается отдельно в бинарную строку длиной 4 байта, все полученный бинарные строки складываются в одну итоговую строку и эта строка пересылается клиенту. У клиента эта строка трактуется как бинарный массив Uint32Array(binStr).
В этой схеме логично использовать зеркальные функции gmp_export() и gmp_import().
Относительно замечания @iluuu1994 "It's definitely not safe to just concatenate exported GMP numerals since they're variable-length and not self-delimiting" оно ошибочно. Бинарные строки получаемые в результате GMP упаковки имеют постоянную длину и эта длина известна она декларируется в параметре функции $word_size и позволяет разделять упакованную строку на отдельные числа при распаковке.
Вообще описание функции GMP_EXPORT в документации это сборник загадок.
gmp_export(GMP|int|string $num, int $word_size = 1, int $flags = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN): string
Параметры функции GMP
num - GMP-число для экспорта.
Загадка #1. Про int и string нет ни каких пояснений, что они из себя представляют. Можно конечно строить догадки, но это плохой стиль для документации.
word_size- По умолчанию равно 1. Количество байт в каждом блоке бинарных данных. Обычно используется совместно с заданием параметра options.
Загадка #2. Каждый блок бинарных данных - а что такое блок? а их несколько? а сколько? а что в каждом их них содержится?
Загадка #3. Параметр options вообще нигде ни до ни после этого места не фигурирует, думай что хочешь.
Загадка #4. Ни каких ограничений на значение word_size не приводится, хотя очевидно что это не так, оно не может быть произвольным числом.
MorganLOCode commentedon Nov 4, 2025
Are you suggesting this is more a documentation bug? There is a user note from five years ago giving examples of the various flags' effects that could perhaps be folded into the main page. I agree that the constants should be better described, since they aren't the same as the 1/-1/0 values used by GMP's
mpz_export.GMP_MSW_FIRSTGMP_LSW_FIRSTGMP_LITTLE_ENDIANGMP_BIG_ENDIANGMP_NATIVE_ENDIANNot really; 0 serialises to a string of length zero, and numbers greater than 232-1 serialise to strings that are more than four bytes long (so, longer than one word).
The word size and option flags control the order in which bytes appear within the string (see the user note mentioned above), not the string length.
Since you're not using multiprecision arithmetic, it would seem more logical to use the already-mentioned pack() (and unpack() if you're deserialising in PHP as well).
http://www.php.net/pack
http://www.php.net/unpack
aub2000 commentedon Nov 11, 2025
Спасибо за обсуждение! Многое прояснил для себя в результате общения. Только жаль что документация такая некачественная.