Во-первых, стоит освежить знания о
бинарном представлении целых знаковых чисел. В 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
.
Беззнакового сдвига влево не существует, потому что он
совпадал бы со знаковым сдвигом, и был бы избыточным.