Принудительное переполнение целых чисел PHP

У нас есть целочисленная арифметика, которая по историческим причинам должна работать на PHP так же, как на нескольких статически типизированных языках. Начиная с последнего обновления PHP, поведение для переполненных целых чисел изменилось. В основном мы используем следующую формулу:

function f($x1, $x2, $x3, $x4) { return (($x1 + $x2) ^ $x3) + $x4; } 

Однако даже при конверсиях:

 function f($x1, $x2, $x3, $x4) { return intval(intval(intval($x1 + $x2) ^ $x3) + $x4); } 

Я все еще получаю совершенно неправильный номер …

Например, с $ x1 = -1580033017, $ x2 = -2072974554, $ x3 = -1170476976) и $ x4 = -1007518822, в итоге я получаю -30512150 в PHP и 1617621783 в C #.

Просто добавляя вместе $ x1 и $ x2, я не могу получить правильный ответ:

В C # я получаю

 (-1580033017 + -2072974554) = 641959725 

В PHP:

 intval(intval(-1580033017) + intval(-2072974554)) = -2147483648 

это то же самое, что:

 intval(-1580033017 + -2072974554) = -2147483648 

Я не против писать функцию IntegerOverflowAdd или что-то в этом роде, но я не могу понять, как (-1580033017 + -2072974554) равно 641959725. (Я действительно признаю, что это -2147483648 + (2 * 2 ^ 31) , но -2147483648 + 2 ^ 31 – -1505523923, что больше, чем Int.Min, так почему вы добавляете 2 * 2 ^ 31, а не 2 ^ 31?)

Любая помощь будет оценена …

Поэтому я решил проблему и много узнал о PHP (по крайней мере, так, как он справляется с переполнением Integer).

1) Это полностью зависело от того, на какой платформе работает машина, в какой версии PHP, независимо от того, был ли запущен Suhosin Hardened PHP, и сколько битов было скомпилировано для (32 или 64). 6 велись так, как я ожидал (что было фактически неправильно, по крайней мере, неправильно в соответствии с их документацией), и 3 машины вели себя так, как я все еще не могу объяснить, и 3 машины вели себя в соответствии с тем, что говорит команда intval в документация.

2) Предполагается, что Intval вернет PHP_MAX_INT, когда int> PHP_MAX_INT (не int & 0xffffffff), но это происходит только в некоторых версиях PHP4 и PHP5. Различные версии PHP возвращают разные значения при int> PHP_MAX_INT.

3) Следующий код может возвращать 3 разных результата (см. 1):

 <?php echo "Php max int: ".PHP_INT_MAX."\n"; echo "The Val: ".(-1580033017 + -2072974554)."\n"; echo "Intval of the val: ".intval(-3653007571)."\n"; echo "And 0xffffffff of the val: ".(-3653007571 & 0xffffffff)."\n"; ?> 

Он может вернуться (что кажется правильным для Intval, но неверно для & 0xffffff)

 Php max int: 2147483647 The Val: -3653007571 Intval of the val: -2147483648 And of the val: -2147483648 

И он может вернуться (что противоречит документации PHP для intval):

 Php max int: 2147483647 The Val: -3653007571 Intval of the val: -641959725 And of the val: -641959725 

И на 64-битных машинах возвращается (что правильно):

 Php max int: 2147483647 The Val: -3653007571 Intval of the val: -3653007571 And of the val: -641959725 

Решение

Во всяком случае, мне нужно решение, которое будет работать на всех этих платформах, и не будет зависеть от особенностей конкретной версии PHP, скомпилированной с конкретным Max int. Таким образом, я использую следующую кросс-PHP функцию тридцать TwwBitIntval:

 function thirtyTwoBitIntval($value) { if ($value < -2147483648) { return -(-($value) & 0xffffffff); } elseif ($value > 2147483647) { return ($value & 0xffffffff); } return $value; } 

Комментарий

Я считаю, что разработчики PHP должны сказать, что Int – это 32-битный Int, независимо от того, работает ли он на 32-разрядной или 64-битной машине (например, в среде DOTNet CLR) и не случайно повышал ее до float в зависимости от количества битов, которые PHP является компилятором.

Если вы хотите иметь 100% -ное рабочее решение для 32-битного intval как на 32 и 64-битных платформах, я предлагаю вам использовать следующее решение:

 function intval32bits($value) { $value = ($value & 0xFFFFFFFF); if ($value & 0x80000000) $value = -((~$value & 0xFFFFFFFF) + 1); return $value; } 

Внутри PHP для большинства чисел использует «целочисленный» тип. Тем не менее, они только заходят так далеко: если вы добавите большое целое число в большое целое число, PHP увидит, что результат слишком велик, чтобы вписаться в нормальное целое число и назначит его числу с плавающей запятой. Однако числа с плавающей запятой (float) только поднимаются так высоко, и есть точка вокруг шестнадцатизначной метки, где PHP просто потеряет сюжет целиком.

Существует возможность использовать математику произвольной точности, которая поддерживает числа любого размера и точности, представленные в виде строк . Подробнее см. Здесь: http://us2.php.net/bc

Я думаю, что это может быть связано с тем, что целое число в PHP составляет 32 бита без знака, так как в C # они по умолчанию 32 бита.

Вы играете с числами на краю нормального диапазона 31-32 бит.

См. Дополнительную документацию в руководстве по PHP:

http://www.php.net/manual/en/language.types.integer.php

Размер целого зависит от платформы, хотя максимальное значение около двух миллиардов – это обычное значение (это 32 бита). PHP не поддерживает целые числа без знака. Целочисленный размер может быть определен с использованием константы PHP_INT_SIZE и максимального значения с использованием константы PHP_INT_MAX с PHP 4.4.0 и PHP 5.0.5.

Будет ли это работать?

 echo (-1580033017 + -2072974554) & 0xffffffff 

Чтобы обобщить, вы могли бы сделать (простить любые синтаксические ошибки, я не дотрагивался до PHP в течение длительного времени):

 function s32add($a, $b) { return ($a + $b) & 0xffffffff; } 

Проверьте свой номер версии PHP – я полагаю, вы можете получить разные результаты с разными версиями PHP, которые могут иметь разную поддержку для длинных целых чисел. Я считаю, что была ошибка с длинными целыми числами, наконец, в одной из версий PHP 5.

В версии PHP 5.2.0 – ответ ТОЧНО так же, как вы получили в C #

1617621783,

используя точную функцию, которую вы имеете выше.

Вы можете использовать команду phpinfo (), чтобы легко найти номер своей версии.

 $x1 = -1580033017; $x2 = -2072974554; $x3 = -1170476976 ; $x4 = -1007518822; echo f($x1, $x2, $x3, $x4); function f($x1, $x2, $x3, $x4) { return intval(intval(intval($x1 + $x2) ^ $x3) + $x4); }