PHP, как известно, язык не имеющий строгой типизации переменных, как, например, С или С++. Объявляете переменную, пихаете в нее данные, а интерпретатор сам решает, в каком виде их хранить. Однако так же присутствует и механизм, позволяющий специально преобразовать переменную из одного типа в другой – так называемое приведение типов.
Что касается целых чисел, интерпретатор может определить, что результат операции не поместится в переменную и конвертирует ее в другой тип. В PHP нет поддержки беззнаковых чисел (uint, unsigned integer) и результат операции может быть автоматически помещен в float (double).
Но во время операции побитового сдвига (bitwise shift) влево (<<) мы легко можем выйти за границы integer и неожиданно получить отрицательное число. Приведу пример кода:
<?php $p = 1818396875; $q = $p; echo "\n\n\n\$p = (" . gettype($p) . ") $p\n"; echo "Executing \$p = (float)\$p;\n"; $p = (float)$p; echo "\$p = (" . gettype($p) . ") $p\n"; echo "Binary value is: " . decbin( $p ) . "\n"; echo "Executing \$p = \$p << 1;\n"; $p = $p << 1; echo "\$p = (" . gettype($p) . ") $p\n"; echo "Binary value is: " . decbin( $p ) . "\n"; echo "Converting \$p via bindec( decbin( \$p ) )\n"; $p = bindec( decbin( $p ) ); echo "\$p = (" . gettype($p) . ") $p\n"; echo "Binary value is: " . decbin( $p ) . "\n\n"; echo "\$q = (" . gettype($q) . ") $q\n"; echo "Binary value is: " . decbin( $q ) . "\n"; echo "Executing \$q = \$q * 2;\n"; $q = $q * 2; echo "\$q = (" . gettype($q) . ") $q\n"; echo "Binary value is: " . decbin( $q ) . "\n\n\n\n"; ?>
Результат выполнения данного кода:
$p = (integer) 1818396875 Executing $p = (float)$p; $p = (double) 1818396875 Binary value is: 1101100011000101000100011001011 Executing $p = $p << 1; $p = (integer) -658173546 Binary value is: 11011000110001010001000110010110 Converting $p via bindec( decbin( $p ) ) $p = (double) 3636793750 Binary value is: 11011000110001010001000110010110 $q = (integer) 1818396875 Binary value is: 1101100011000101000100011001011 Executing $q = $q * 2; $q = (double) 3636793750 Binary value is: 11011000110001010001000110010110
Как видно, после математической операции умножения переменная оказалась типа double.
Увы, это не бага, а фича, в документации об этом прямо прописано: “Оба операнда и результат выполнения << и >> всегда считаются за целое.”.
Просто нужно об этом знать при работе с большими числами.