JavaRush /Курсы /Java Syntax Pro /Побитовые операции, часть 2

Побитовые операции, часть 2

Java Syntax Pro
9 уровень , 6 лекция
Открыта

1. Побитовый сдвиг влево

Также в Java есть 3 оператора побитового сдвига числа. Вы можете очень просто сдвинуть все биты числа на несколько позиций влево или вправо, если вам это, конечно, нужно.

Чтобы сдвинуть биты числа влево, нужно использовать оператор побитового сдвига влево. Записывается это так:

a << b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит влево нужно сдвинуть биты числа a. При этом справа к числу добавляются нулевые биты.

Примеры:

Пример Результат
0b00000011 << 1
0b00000110
0b00000011 << 2
0b00001100
0b00000011 << 5
0b01100000
0b00000011 << 20
0b001100000000000000000000

Сдвиг на один разряд влево дает тот же эффект, что и умножение числа на 2.

Хотите умножить число на 16 ? 16 = это 24. Значит нужно сдвинуть число на 4 разряда влево


2. Побитовый сдвиг вправо

Так же биты можно сдвигать вправо. Для этого используется оператор побитового сдвига вправо. Записывается он так:

a >> b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит вправо нужно сдвинуть биты числа a.

Примеры:

Пример Результат
0b11000011 >> 1
0b01100001
0b11000011 >> 2
0b00110000
0b11000011 >> 5
0b00000110
0b11000011 >> 20
0b00000000

Сдвиг на один разряд вправо дает тот же эффект, что и деление числа на 2 нацело.

При этом слева к числу добавляются нулевые биты, но не всегда!

Важно!

Самый левый бит числа называется битом знака: хранит 0, если число положительное и 1, если число отрицательное.

При сдвиге битов числа вправо, бит знака тоже сместится, и знак числа потеряется. Поэтому для отрицательных чисел (у которых самый левый бит равен 1), этот бит специально сохраняется. При сдвиге бит числа вправо, слева число дополняется битом 0, если самый левый бит был 0, и битом 1, если самый левый бит был 1.

Но в примере выше этого не происходит. Почему? Все дело в том, что целочисленные литералы имеют тип int и 0b11111111 на самом деле значит 0b00000000000000000000000011111111. Т.е. самый левый бит равен нулю.

Многих программистов расстраивает такое поведение сдвига вправо, и они хотели бы, чтобы число всегда дополнялось нулевыми битами. Поэтому в Java добавили еще один оператор сдвига вправо.

Записывается он так:

a >>> b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит вправо нужно сдвинуть биты числа a. Этот оператор всегда дописывает слева нулевые биты, независимо от того, какой бит знака был у числа a.


9
Задача
Java Syntax Pro, 9 уровень, 6 лекция
Недоступна
Степень двойки
Реализуй метод getPowerOfTwo(int power), который возвращает число 2 в степени power. Тебе нужно изменить реализацию метода getPowerOfTwo(int), используя соответствующий побитовый сдвиг.

3. Работа с флагами

На основе побитовых операций и операций сдвига программисты реализовали чуть ли не целое новое направление — работа с флагами.

Во времена, когда памяти в компьютерах было очень мало, было очень популярно впихивать в одно число кучу информации. При этом число рассматривалось как массив бит: int — это 32 бита, long — 64 бита.

В такое число можно много всего записать, особенно если нужно хранить логическую информацию: true или false. Один long — это как массив boolean на 64 элемента. Такие биты назывались флагами и нуждались в операциях:

  • установить флаг
    (сделать определенный бит равным 1)
  • сбросить флаг
    (сделать определенный бит равным 0)
  • проверить флаг
    (проверить, чему равен определенный бит)

И вот как это делается с помощью побитовых операторов.

Установка флага

Чтобы установить определенный бит в 1, нужно выполнить операцию побитового ИЛИ между числом, в котором нужно установить этот бит и специальным числом, где только этот бит равен 1.

Например, вам нужно в числе 0b00001010 установить 5-й бит в 1, тогда нужно сделать так:

0b00001010 | 0b00010000 = 0b00011010

Если 5-й бит уже был установлен в единицу, ничего бы не поменялось.

В общем виде операцию установки флага можно записать так

a | (1 << b)

Где a — это число, внутри которого определенный бит устанавливается в 1. А b — это индекс устанавливаемого бита. Очень удобно использовать тут сдвиг влево – сразу видно, с каким битом мы работаем.

Сброс флага

Чтобы сбросить определенный бит в 0 и не потревожить другие биты, нужно выполнить операцию & между числом, в котором нужно сбросить бит в 0 и специальным числом, где все биты равны 1, кроме заданного.

Например, вам нужно в числе 0b00001010 установить 4-й бит в 0, тогда нужно сделать так:

0b00001010 & 0b11110111 = 0b00000010

Если 4-й бит уже был сброшен в ноль, ничего бы не поменялось.

В общем виде операцию сброса флага можно записать так

a & ~(1 << b)

Где a — это число, внутри которого сбрасывается в 0 определенный бит. А b — это индекс сбрасываемого бита.

Чтобы получить число, у которого все биты равны 1, а нужный нам равен нулю, мы сначала сдвигаем единицу на b позиций влево, а затем инвертируем биты числа с помощью побитовой операции НЕ.

Проверка флага

Кроме установки или сброса определенного флага, иногда бывает нужно просто проверить, установлен ли данный флаг – равен ли определенный бит 1. Это достаточно легко сделать с помощью побитовой операции &.

Например, вам нужно в числе 0b00001010 проверить, установлен ли 4-й бит в 1. Тогда нужно сделать так:

if ( (0b00001010 & 0b00001000) == 0b00001000 )

В общем виде операцию проверки флага можно записать так:

(a & (1 << b)) == (1 << b)

Где a — это число, внутри которого находится проверяемый бит. А b — это индекс проверяемого бита.


4. Шифрование

Побитовая операция XOR часто применяется программистами для самого простого шифрования. В общем виде операция шифрования выглядит так:

результат = число ^ пароль;

Где число — это данные, которые мы хотим зашифровать, пароль — специальное число, используемое в качестве «пароля» к данным, а результат — зашифрованное число.

число == (число ^ пароль) ^ пароль;

Все дело в том, что оператор XOR, дважды примененный к числу, дает исходное число, независимо от пароля.

Чтобы получить искомое число из зашифрованного, нужно просто выполнить операцию повторно:

оригинальное число = результат ^ пароль;

Пример:

class Solution
{
   public static int[] cript(int[] data, int password)
   {
     int[] result = new int[data.length];
     for (int i = 0; i <  data.length; i++)
       result[i] = data[i] ^ password;
     return result;
   }

   public static void main(String[] args)
   {
     int[] data =  {1, 3, 5, 7, 9, 11};
     int password = 199;

     // зашифровываем массив данных
     int[] encrypted = cript(data, password) ;
     System.out.println(Arrays.toString(encrypted));

     // расшифровываем массив данных
     int[] decrypted = cript(encrypted, password) ;
     System.out.println(Arrays.toString(decrypted));
   }
}

9
Задача
Java Syntax Pro, 9 уровень, 6 лекция
Недоступна
Флаги
Реализуй методы: 1. setFlag(int number, int flagPos) - устанавливает значение "1" биту под индексом flagPos числа number и возвращает новое значение. 2. resetFlag(int number, int flagPos) - устанавливает значение "0" биту под индексом flagPos числа number и возвращает новое значение. 3. checkFlag(in
Комментарии (359)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
w5277c Уровень 17
29 ноября 2024
(a & (1 << b)) == (1 << b) А может так? 0 != (a & (1 << b)) часто применяется программистами для самого простого шифрования А для не простого не применяется? Так себе преподаватели. Боюсь представить чему они дальше научат.
JaCat Уровень 10
17 ноября 2024
setFlag int result = number | (1 << flagPos); Берется 1 (00000001) и двигается на flagPos позиций влево, дальше через бинарное ИЛИ(|) устанавливается в нужном месте. Например: number = 5; flagPos = 3; Берем 1 = 00000001 сдвигаем его на 3 позиции влево (1 << flagPos) и получаем 00001000 - это 8 если интересно) Получившийся результат сравниваем с number по бинарному ИЛИ(|): 00000101 - 5 00001000 - 8 *************** 00001101 - 13 resetFlag int result = number & ~(1 << flagPos); Берется 1 (00000001) и двигается на flagPos позиций влево, дальше меняется через логическое НЕ(~) на 0 и устанавливается в нужном месте через бинарное И(&).
JaCat Уровень 10
17 ноября 2024
2 << (power-1)
Andrey Уровень 27
15 октября 2024
Читайте последнюю задачу внимательно. Там flagPos это ИНДЕКС а не позиция бита как я подумал. (не надо вычитать/прибавлять 1). Делайте как в учебнике будет вам щастье)
Antariko Уровень 14
7 сентября 2024
Тема в целом несложная. Операции все понятны. Непонятно для чего это? Где и зачем применять массивы - понятно. Циклы, классы, объекты, формулы и тд - понятно. Куда и зачем вот это?... Ну ок за исключением криптографии. Рядовому программисту где это может понадобится? Есть здесь кто уже работает? Где и зачем вам понадобились эти знания, можете рассказать? Просто ради примера...
Crypton Уровень 12
10 сентября 2024
Чем больше знаешь, тем шире твой кругозор
Antariko Уровень 14
10 сентября 2024
А пролистав пару лекций сам сайт говорит о том, что "Чем шире кругозор в начале обучения, тем медленнее ты становишься специалистом".
Антон Хатунцев Уровень 10
25 августа 2024
Где-то в глубине сознания мне понятен процесс побитовых операций. Но мой мозг завтра же сотрет эту информацию из памяти, т.к. не понятно для чего эти операции с битами вообще применяются!!!
IWan Уровень 12
2 сентября 2024
Для оптимизации системы. Хоть и у нас стали боле мощные машины, но все же оптимизировать программу нужно всегда, и как можно сильнее. Нынешние разработчики, опираются на мощность системы и стараются обходить все подобные нюансы. За счет чего и выходят игры с графикой ниже уровня игр 2012 года, а требовательными как для 2030 года. Но я конечно же солгу что это происходит только с играми. С приложениями это конечно не так заметно. 👾
Andrey Shepel Уровень 23
29 сентября 2024
Ох и холиварную тему вы затронули. Разработчики пишут код в первую очередь не для компьютера, а для человека. Поэтому нет смысла оптимизировать код там, где это даже не будет заметно. Мне кажется мало полезной оптимизация, которая сэкономит суммарно всем пользователям 1 секунду, и затратит несколько человеко-дней разработчиков, увеличив трудозатраты в понимании кода и в работе с ним. В gameDev, вероятно, другая ситуация.
Anonymous #3073331 Уровень 19
25 октября 2024
Я буквально спросил у мужа старшего джависта - он ответил, что кое-где используется, но это очень нишевые области, типа игры или низкоуровневые сетевые протоколы.
Семён Уровень 9
24 августа 2024
Работаю более 3 лет на PHP. Считаю что из всего этого, на практике, применимы только флаги, их есть смысл понять. Остального я почти не встречал, думаю большенству (первое время точно) не пригододиться (только если на собесе, и то скорее всего нет). Если вы ничего не поняли, то думаю можно и флаги пропустить, не критично. Если все-таки хочется понять хотя бы флаги, то вот ссылка на статью на habr по работе с флагами в PHP, где понятно изложен минимум, которого будет достаточно что бы выполнить последний квест
andreyJdev Уровень 15
23 августа 2024
По битам лекции сделаны будто для людей, которые в этом всем уже разбираются. Вот я окончил вышку и для меня записи вроде 0b11000011 не были чем-то диким, да и видео в начале курса с лампочками помогло вспомнить, как все строится. А новичок вообще не поймет, что это за длинная запись, так как нигде не объясняется, как строится двоичная запись чисел за исключением короткого ролика на первом уровне, о котором 90% людей уже забыли.
Honeybadger Уровень 26
20 августа 2024
это бы объяснение, да в предыдущей лекции...к задаче про swap - залетело бы, как слива в жопу)
NightCrow Уровень 27
5 сентября 2024
Интересно, а зачем вы суете сливы в неположенные места? 😦
Duxa552 Уровень 46
15 августа 2024
Всем, то мало что понял из лекции (как я), рекомендую статью на хабре https://habr.com/ru/companies/metalamp/articles/809549/