В 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