PHP и побитовый сдвиг

Автор: | 5 октября 2016

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.

Увы, это не бага, а фича, в документации об этом прямо прописано: “Оба операнда и результат выполнения << и >> всегда считаются за целое.”.

Просто нужно об этом знать при работе с большими числами.

Добавить комментарий