Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Побитовые операторы

Любое целое число хранится в памяти как последовательность битов — нулей и единиц. Побитовые операторы работают не с числом целиком, а с каждым его битом по отдельности. Их применяют для флагов, масок, кодирования данных и быстрых вычислений.

Двоичная запись

Метод Integer.toBinaryString() показывает число в двоичном виде. А в коде двоичное число записывают с префиксом 0b:

void main() {
    IO.println(Integer.toBinaryString(5));
    IO.println(Integer.toBinaryString(12));
    IO.println(0b1010);
}

Результат:

101
1100
10

Обратно строку из нулей и единиц в число превращает Integer.parseInt("1010", 2) — второй аргумент задаёт систему счисления.

И, ИЛИ, исключающее ИЛИ

Операторы сравнивают биты двух чисел попарно. & (И) даёт 1, только если оба бита равны 1; | (ИЛИ) — если хотя бы один равен 1; ^ (исключающее ИЛИ) — если биты разные:

void main() {
    int a = 0b1100;
    int b = 0b1010;
    IO.println(Integer.toBinaryString(a & b));
    IO.println(Integer.toBinaryString(a | b));
    IO.println(Integer.toBinaryString(a ^ b));
}

Результат:

1000
1110
110

Не путайте их с логическими && и || из урока об операторах: те работают с boolean и не вычисляют правую часть, если ответ уже известен.

Инверсия ~

Оператор ~ переворачивает каждый бит: 0 становится 1, а 1 — нулём. Поскольку старший бит int отвечает за знак, результат получается отрицательным: ~n всегда равно -n - 1:

void main() {
    IO.println(~5);
    IO.println(Integer.toBinaryString(~5));
}

Результат:

-6
11111111111111111111111111111010

Сдвиги

<< сдвигает биты влево, дописывая справа нули, — это умножение на 2 для каждой позиции. >> сдвигает вправо с сохранением знака — деление на 2. >>> тоже сдвигает вправо, но слева всегда дописывает нули, даже у отрицательных чисел:

void main() {
    IO.println(1 << 3);
    IO.println(40 >> 2);
    IO.println(-16 >> 2);
    IO.println(-16 >>> 28);
}

Результат:

8
10
-4
15

Флаги и маски

Каждый бит числа может хранить отдельный признак «да/нет» — флаг. Число с нужными битами называется маской. | включает флаг, & проверяет его, а & ~ выключает:

void main() {
    final int READ = 0b001;
    final int WRITE = 0b010;
    final int EXECUTE = 0b100;
 
    int access = READ | WRITE;
    IO.println("Чтение: " + ((access & READ) != 0));
    IO.println("Запуск: " + ((access & EXECUTE) != 0));
 
    access = access & ~WRITE;
    IO.println(Integer.toBinaryString(access));
}

Результат:

Чтение: true
Запуск: false
1

В одном int помещается 32 таких флага. В обычном коде вместо масок чаще используют EnumSet — о нём урок в главе о коллекциях.

Частая ошибка

Сравнение != выполняется раньше, чем &. Без скобок Java пытается вычислить WRITE != 0, а потом применить & к числу и boolean:

void main() {
    int access = 0b011;
    int WRITE = 0b010;
    if (access & WRITE != 0) {
        IO.println("Можно писать");
    }
}

Результат:

Main.java:4: error: bad operand types for binary operator '&'
    if (access & WRITE != 0) {
               ^
  first type:  int
  second type: boolean
1 error
error: compilation failed

Всегда берите побитовую часть в скобки: (access & WRITE) != 0.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.