Во-первых, стоит освежить знания о
бинарном представлении целых знаковых чисел. В Java используется подход
two's complement – для значения 0 все биты нули, при переполнении максимального значения на 1 получается минимальное.
Бинарные битовые операторы &,
| и
^ действуют очевидным образом: выполняют побитовые «И», «ИЛИ» и «
исключающее ИЛИ» (XOR) соответственно. Здесь особенно интересен XOR:
- Применение к значению «исключающего или» с одним и тем же параметром два раза дает то же значение. За счет этого его можно использовать как простейшее шифрование, аргумент выступит ключом;
- С помощью XOR реализуется XOR-обмен – алгоритм обмена значениями переменных без дополнительной памяти и без риска переполнения. Это также один из популярных вопросов для собеседования.
Унарный оператор битового отрицания (дополнения) ~. Эквивалентен «исключающему или» с самим собой – все биты инвертируются.
~x эквивалентно
-x-1.
~0 == -1.
Битовые сдвиги: левый
<< правый знаковый
>> и правый беззнаковый
>>>. Левый операнд – что сдвигать, правый – на сколько
битов.
Второй параметр, дистанция сдвига, должен быть не больше доступных разрядов – 31 для
int и 63 для
long. Если передано значение больше – используются младшие 5 и 7 битов соответственно. То есть для
int переменной
x << 33 эквивалентно
x << 2.
a << b эквивалентно умножению
a на 2 в степени
b.
a >> b совпадает с делением на 2 в степени
b, с округлением вниз. Для положительных a то же что
a/pow(2,b). Для
не делящихся нацело на
pow(2,b) отрицательных это
a/pow(2,b)-1.
Беззнаковый сдвиг вправо трактует число
не как two's complement, а как беззнаковое. То есть
Integer.MIN_VALUE будет сдвинут так, как будто это сдвигается число на 1 большее чем
Integer.MAX_VALUE.
Беззнакового сдвига влево не существует, потому что он
совпадал бы со знаковым сдвигом, и был бы избыточным.