Побитовые операции
Иногда требуется обрабатывать числа на уровне битов. Для этих целей существует группа побитовых операций, которые применимы только к целочисленным типам.
В таблице, в порядке убывания приоритетов, приведены все символы и описания побитовых операторов с указанием ассоциативности.
П |
Символы |
Описание |
Пример |
А |
---|---|---|---|---|
2 |
~ |
Побитовое отрицание (инверсия) |
~e1 |
R |
5 |
<< |
Сдвиг влево |
e1 << e2 |
L |
5 |
>> |
Сдвиг вправо |
e1 >> e2 |
L |
8 |
& |
Побитовое И |
e1 & e2 |
L |
9 |
^ |
Побитовое исключающее ИЛИ |
e1 ^ e2 |
L |
10 |
| |
Побитовое ИЛИ |
e1 | e2 |
L |
Из всей группы только операция побитового отрицания '~' является унарной, все остальные — бинарные.
Во всех случаях, если операнд по размеру меньше int/uint, то он предварительно расширяется до int/uint-а, путем добавления 0-битов в старшие разряды. В зависимости от знаковости типа операнда старший бит может влиять на знак.
Понять представление чисел на битовом уровне может помочь стандартное приложение Windows Калькулятор. Если в меню Вид выбрать режим работы Программист, в программе появляются группы переключателей для выбора отображения числа в шестнадцатеричной (Hex), десятичной (Dec), восьмеричной (Oct) и бинарной (Bin) форме. Последняя как раз показывает биты. Кроме того, можно выбрать размер числа: 1, 2, 4 или 8 байт. Кнопки позволяют выполнять все рассматриваемые операции: Not ('~'), And ('&'), Or ('|'), Xor ('^'), Lsh ('<<'), Rsh ('>>').
Поскольку Калькулятор использует знаковые числа, при переключении в десятичный режим возможно появление отрицательных значений (напомним, старший бит интерпретируется как знак). Для удобства анализа имеет смысл исключить появление минуса, для чего необходимо выбирать размер в байтах на одну градацию больше. Например, для проверки значений в диапазоне до 255 (uchar, беззнаковое однобайтовое целое), следует выбрать 2 байта (иначе положительными будут только десятичные значения до 127 включительно, а остальные отобразятся в отрицательную область).
Побитовое отрицание создает значение, в котором на месте всех 1-битов стоит 0-бит, а на месте 0-битов — 1-бит. Например, отрицание байта со всеми нулевыми битами, дает байт со всеми единичными битами. Число 50 в побитовом формате выглядит как '00110010' (байт). Его инверсия дает '11001101'.
Единица в шестнадцатеричном представлении — это 0x0001 (для short). Инверсия этих битов дает 0xFFFE (см. скрипт ExprBitwise.mq5).
short v = ~1; // 0xfffe = -2
|
Побитовое И проверяет каждый бит в обоих операндах, и в тех позициях, где обнаруживаются два взведенных бита (1), сохраняет в результат 1-бит. Во всех остальных случаях (когда взведенный бит есть только в одном операнде или они сброшены и там, и там) в результат записывается 0-бит.
Побитовое ИЛИ записывает в результат 1-биты на тех позициях, на которых взведенный бит есть хотя бы в одном из двух операндов.
Побитовое исключающее ИЛИ записывает в результат 1-биты на тех позициях, на которых взведенный бит есть либо в первом, либо во втором операнде, но не одновременно. Ниже показано бинарное представление двух чисел X и Y и результаты побитовых операций с ними.
X 10011010 154
|
При написании сложных выражений из нескольких разных операторов используйте группировку с помощью круглых скобок, чтобы не путаться с приоритетами.
Операции сдвига перемещают биты влево ('<<') или вправо ('>>') на количество битов, заданное во втором операнде, который должен быть неотрицательным целым числом. В результате левые биты (для '<<') или правые биты (для '>>') отбрасываются, так как выходят за границы ячейки памяти. При сдвиге влево, справа добавляется соответствующее количество 0-битов. При сдвиге вправо, слева добавляются либо 0-биты (если операнд беззнаковый), либо размножается бит знака (если операнд знаковый). Во втором случае для положительных чисел слева добавляются 0-биты, а для отрицательных — 1-биты, то есть, знак сохраняется.
short q = v << 5; // 0xffc0 = -64
|
На примере выше первоначальный сдвиг влево "уничтожил" старшие биты переменной p, а последующий сдвиг вправо на то же количество битов заполнило их нулями, в результате чего значение уменьшилось с 0xffc0 до 0x07fe.
Размер сдвига (количество битов) должен быть меньше размера типа операнда (с учетом возможного расширения). В противном случае все исходные биты будут потеряны.
Сдвиг на 0 битов оставляет число неизменным.
Не следует путать побитовые операции '&' и '|' с логическими операциями '&&' и '||' (рассмотрены в предыдущем разделе).