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
  • Всеволод

    Большое спасибо,отличный урок)

  • Anonimus

    Как-то я не очень понял на счёт экономии памяти в последнем примере.
    «После выполнения, x будет равняться 1101101 (bin).» На сколько я правильно понимаю, то 01101101 — это почти 2 байта.
    ZiGGi, можно ли с Вами связаться через skype для получения более подробного объяснения по использованию побитовых операторов?

    • 01101101 — это 1 байт = 8 бит.
      В данном примере в одной переменной, размером в 1 байт мы храним 2 числа.

  • Pingback: Операторы Pawn | Блог ZiGGi()

  • fabervox

    Мне одному кажется что в таблице истиности написано неправильно?
    0 | 0 = 0
    0 | 1 = 1
    1 | 0 = 1
    1 | 1 = 1
    Я так полагаю не 0 | 1 = 1, а 0 | 0 = 1.
    Ну а статья отличная.

    • fabervox

      ой, и сам опечатался, 0 | 1 = 0.

      Вот интересно ещё, можно ли к чему нибудь в PAWN(или хотя бы в CPP), применить знание карт Карно. А то вообще не знаю зачем диплом получал хД

    • Там написано правильно. Статья в wiki для подтверждения.

  • Mike

    Хорошая статья.
    Есть вопрос, если использовать:

    new test = 0x00000011;
    new test = 3;

    То разницы в плане экономии памяти нету? Или же если в массиве использовать:
    new test[][] = {0x00000011,0x00000011};
    вместо:
    new test[][] = {3,3};

    Благодарю за ответ!

    • Никакой разницы.

      • Mike

        Хорошо. Спасибо. Выходит толку от этого в pawn нету?

        • Есть, но обычному скриптеру можно обойтись и без них.

          • Mike

            Если не трудно, пожалуйста расскажи где именно можно найти применение этому?
            Заранее благодарю!

          • Указано же в статье: оптимизация памяти, например.

Перейти к верхней панели