Pawn. Побитовые операторы

В Pawn, как и в любом языке программирования есть побитовые операторы. В Pawn они полностью идентичны операторам языка C.

Оператор сдвига влево (<<)

Этот оператор применяется для сдвига битов влево.
Пример:

new x = 3; // 3 = 11 (bin)
x = x << 2; // смещаем биты на 2 бита влево

После выполнения, x будет равняться 1100 (bin) и следовательно 12 (dec).

Для этого оператора доступен сокращённый синтаксис:

new x = 3; // 3 = 11 (bin)
x <<= 2; // смещаем биты на 2 бита влево

Результат выполнения кода будет идентичен вышеописанному.

Оператор сдвига вправо (>>)

Этот оператор применяется для сдвига битов вправо.
Пример:

new x = 3; // 3 = 11 (bin)
x = x >> 1; // смещаем биты на 1 бит вправо

После выполнения, x будет равняться 1 (bin) и следовательно 1 (dec).

Для этого оператора доступен сокращённый синтаксис:

new x = 3; // 3 = 11 (bin)
x >>= 1; // смещаем биты на 1 бит вправо

Результат выполнения кода будет идентичен вышеописанному.

Оператор побитовой инверсии (~)

Этот оператор преобразует бит 1 в бит 0, а бит 0 в бит 1, всё просто.
Пример:

new x = 10; // 10 = 0000 0000 0000 0000 0000 0000 0000 1010 (bin)
x = ~x; // преобразуем бит 1 в бит 0, а бит 0 в бит 1

После выполнения, x будет равняться 1111 1111 1111 1111 1111 1111 1111 0101 (bin) и следовательно -11 (dec).

Оператор логического ИЛИ (|)

Этот оператор обычно применяется для «склейки» битов и для установления битам значения 1.
Ниже приведена таблица истинности для оператора |.
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
Пример:

new x = 9; // 9 = 1001 (bin)
x = x | 7; // 7 = 111 (bin), следовательно 1001 | 111 = 1111

После выполнения, x будет равняться 1111 (bin) и следовательно 15 (dec).

Для этого оператора доступен сокращённый синтаксис:

new x = 9; // 9 = 1001 (bin)
x |= 7; // 7 = 111 (bin), следовательно 1001 | 111 = 1111

Результат выполнения кода будет идентичен вышеописанному.

Можно заметить, что таким способом работать с битами по их номеру достаточно неудобно, к счастью, это легко изменить:

new x = 9; // 9 = 1001 (bin)
x |= ((1 << 1) | (1 << 2)); // устанавливаем значение 1 первому и второму биту

После выполнения, x будет равняться 1111 (bin) и следовательно 15 (dec).

Оператор логического И (&)

Этот оператор обычно применяется для обнуления битов.
Ниже приведена таблица истинности для оператора &.
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
Пример:

new x = 15; // 15 = 1111 (bin)
x = x & 12; // 12 = 1100 (bin), следовательно 1111 & 1100 = 1100

После выполнения, x будет равняться 1100 (bin) и следовательно 12 (dec).

Для этого оператора доступен сокращённый синтаксис:

new x = 15; // 15 = 1111 (bin)
x &= 12; // 12 = 1100 (bin), следовательно 1111 & 1100 = 1100

Результат выполнения кода будет идентичен вышеописанному.

Можно заметить, что таким способом обнулять биты по их позиции достаточно неудобно, к счастью, это легко изменить:

new x = 15; // 15 = 1111 (bin)
x &= ~(1 << 2); // устанавливаем биту под номером 2 значение равное 0

После выполнения, x будет равняться 1011 (bin) и следовательно 11 (dec).

Обнулить несколько битов можно следующим способом:

new x = 15; // 15 = 1111 (bin)
x &= ~((1 << 1) | (1 << 2)); // обнуляем 1 и 2 биты

После выполнения, x будет равняться 1001 (bin) и следовательно 9 (dec).

Оператор исключающего ИЛИ (^)

Этот оператор обычно применяется для инвертации одного или нескольких битов переменной.
Ниже приведена таблица истинности для оператора ^.
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
Пример:

new x = 13; // 13 = 1101 (bin)
x = x ^ 6; // 6 = 110 (bin), следовательно 1101 ^ 110 = 1011
// здесь мы инвертировали 2 и 1 бит переменной

После выполнения, x будет равняться 1011 (bin) и следовательно 11 (dec).

Для этого оператора доступен сокращённый синтаксис:

new x = 13; // 13 = 1101 (bin)
x ^= 6; // 6 = 110 (bin), следовательно 1101 ^ 110 = 1011
// здесь мы инвертировали 2 и 1 бит переменной

Результат выполнения кода будет идентичен вышеописанному.

Можно заметить, что таким способом инвертировать биты по их позиции достаточно неудобно, к счастью, это легко изменить:

new x = 13; // 13 = 1101 (bin)
x ^= ((1 << 1) | (1 << 2)); // инвертируем 1 и 2 биты

После выполнения, x будет равняться 1011 (bin) и следовательно 11 (dec).

Где это может реально пригодиться?

Это может пригодиться для существенной экономии памяти. Например вам доступна 1 ячейка (4 байта) и вам надо записать 2 числа в пределах 0..15. То вы с лёгкостью можете записать их в 1 байт. А в одну ячейку, соответственно, поместится 8 таких чисел.

new x; // создаём переменную, её размер равен 1 ячейке (4 байта)
// запишем число 13 в правые 4 бита и 6 в левые 4 бита
// 13 = 1101 (bin)
// 6 = 110 (bin)
x = ((6 << 4) | (13)); // сдвигаем 3 бита цифры 6 в левую часть байта и ставим 4 бита числа 13 в правые 4 бита
// здесь рекомендуется сделать защиту "от дурака", который может
// попытаться записать в правую часть число размером более 4 битов
// делается это так: x = ((6 << 4) | (13 & 15));
// т.к. 15 == 1111 (bin), то мы обнуляем все биты, за этими пределами

После выполнения, x будет равняться 1101101 (bin).
Записали, теперь надо извлечь:

printf("6 = %d", (x >> 4) & 15); // смещаем вправо на 4 бита, т.к. число 6 у нас в левой части байта, обнуляем левую часть
printf("13 = %d", x & 15); // обнуляем левую часть байта, путём применения логической операции И(в математике её действие идентично умножению), для понятия принципа это можно записать так: (x >> 0) & 15
Перейти к верхней панели