Битовые операции (часть 1, сдвиг влево и вправо)
📌 В мире PHP эти операции встречаются редко, однако в статьях, книгах, либках и других источниках легко можно встретить запись типа:
~
$memory = memory_get_usage() >> 20;
И тут наступает ступор, что за ">>"? Лезешь в доку а там
$a >> $b — Сдвиг вправо. Все биты переменной $a сдвигаются на $b позиций вправо (каждая позиция подразумевает "деление на 2").
❓ Чё сдвигается, куда? Зачем вообще оно надо? Давайте разбираться.
Думаю многие знают, что число в двоичной (бинарной) системе исчисления представляет собой набор нулей и единиц. Например число 6 будет представлено как 00000110.
👍 Побитовый сдвиг в PHP - это арифметическая операция. Биты, сдвинутые за границы числа, отбрасываются. Сдвиг влево дополняет число нулями справа, сдвигая в то же время знаковый бит числа влево, что означает что знак операнда не сохраняется. Сдвиг вправо сохраняет копию сдвинутого знакового бита слева, что означает что знак операнда сохраняется.
Возвращаемся к нашему оператору >>
$n = 6; // 00000110
$k = $n >> 1; // 00000011
👉 Здесь четко видно, что мы отбросили самый правый бит, а слева дополнили нулём. Соответственно запись 00000011 в двоичном представлении равна 3 в десятичном. То есть фактически сдвинув один бит — мы поделили на 2, а если сдвинем на 2 бита, то еще раз поделим на 2 (то есть на 4), на 3 бита — получится что на 8.
❗️ Стоп. Да это же степени двойки!
Возвращаясь к нашему первому примеру >> 20, получится, что мы делим наше исходное значение на 2 в степени 20. Легко запомнить, что 2^10 = 1024. Тогда:
$memory = memory_get_usage() / (1024 * 1024);
Так как функция memory_get_usage() возвращает значение в байтах, то мы всего-лишь перевели всё в Мб. Получается достаточно удобно:
>> 10 приводит в Кб
>> 20 приводит в Мб
>> 30 приводит в Гб
В то время как сдвиг вправо означает деление, то сдвиг влево — умножение.
$y = 5; // 000000101
echo $y << 2; // 000010100 (5 * 4 = 20)
❤️ Однако, самое вкусное использование побитовых операций заключается не в умножении и делении, а именно в использовании побитовой маски, например для разграничений прав доступа, или других подобных операций:
define('U_READ', 1 << 0); // 0001
define('U_CREATE', 1 << 1); // 0010
define('U_EDIT', 1 << 2); // 0100
define('U_DELETE', 1 << 3); // 1000
define('U_ALL', U_READ | U_CREATE | U_EDIT | U_DELETE); // 1111
Выглядит интересно, но об этом и других битовых операциях мы поговорим в следующей части :)
#php #junior #source