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

Задания раздела 4.8
4.25. Каково значение выражения ~'q' << 6 на машине с 32-битовыми целыми числами и 8-битовыми символами, с учетом, что символ 'q' имеет битовое представление 01110001?
4.26. Что будет, если в приведенном выше примере оценки учеников использовать для переменной quiz1 тип unsigned int?
4.27. Каков результат каждого из этих выражений?
unsigned long long ull=3, ul2=7;
(a) ull&ul2
(b) ull | ul2
(c) ull && ul2
(d) ull || ul2

Это моя самая любимая тема. Побитовые операторы шикарны, с ними просто обязан разобраться каждый уважающий себя программист. Но в этой теме всего лишь базовая верхушка побитовой магии. Но всему есть начало. И оно уже здесь.
Для работы с побитовыми операторами необходимо использовать целочисленные беззнаковые операнды.
Первоначальное значение операторов >> << не ввод и вывод из потока, а побитовый сдвиг операнда. Эти операторы возвращают значение, являющееся копией левого операнда, биты которого сдвинуты. Правый операнд не должен быть отрицательным, и его значение должно быть меньше количества битов результата. Биты сдвигаются влево (<<) или вправо (>>),  при этом вышедшие за пределы биты отбрасываются.
Оператор сдвига влево (<<) добавляет нулевые биты справа.
Оператор сдвига вправо (>>) зависит от типа левого операнда, если  он беззнаковый, то оператор добавляет слева нулевые биты, если же операнд знаковый, то результат зависит от конкретной реализации - слева вставляются либо копии знакового разряда, либо нули. Поэтому советуют не использовать знаковые операнды как объект для сдвига.
Побитовые операторы:
~ Побитовое NOT
<<  сдвиг влево
>>  сдвиг вправо
&  Побитовое AND
^  Побитовое XOR
|  Побитовое OR

Что делает побитовый сдвиг уже сказал. Двигает биты влево или вправо внутри ячейки, зарезервированной под операнд, памяти.
Побитовое ~ NOT инвертирует биты. Вот прямо побитово превращает 0 в 1 и 1 в 0.
Побитовые & AND | OR и ^ XOR применяются к двум операндам и создают новое значение.
Побитовое & AND берет соответствующие позиции битов двух операндов и выдает результатом бит:

& AND: содержит 1, если оба операнда содержат 1 в этой позиции; в противном случае результат - 0;
1&1 = 1
1&0=0
0&1=0
0&0=0

& OR: содержит 1, если один или оба операнда содержат 1 в этой позиции; в противном случае результат - 0;
1 | 1=1
1 | 0=1
0 | 1=1
0 | 0=0

^ XOR: содержит 1, если любой но не оба оба операнда содержат 1 в этой позиции; в противном случае результат - 0;
1 ^ 1=0
0 ^ 1=1
1 ^ 0=1
0 ^ 0=0

Побитовые и логические операторы - это разные вещи. Путать их не стоит!

Использовать побитовые операторы можно всякими хитрыми способами. В заданиях необходимо будет поработать с программой оценки учеников из этого раздела, поэтому приведу её здесь.
Значит программа оценки учеников должна делать следующее:
Есть 30 учеников. Каждую неделю у класса зачет, результат которого -"сдал" или "не сдал" - должен храниться по одному биту на ученика. Вот такой вот жадный учитель.
Результаты будут храниться в unsigned long quiz1=0;
Теперь самое главное разобраться как происходит доступ к отдельному биту определенного операнда.
Например, ученик 27 сдал зачет. Значит в позиции 27 переменной quiz1 должна стоять единица. Для начала нужно создать литерал типа unsigned long с единицей в определенной позиции, соответствующей номеру ученика.
1UL << 27
что происходит? создается литерал 1 и записывается в ячейку размером unsigned long. Далее установленный первый бит 1 сдвигается на 27 позиций.
Было:
00000000000000000000000000000001
Получилось:
00000100000000000000000000000000
На самом-то деле этот бит нужен мне в объекте quiz1:
quiz1=quiz1 | 1UL << 27;
можно запись сократить, используя составной оператор:
quiz1 |= 1UL << 27;
Почему вообще побитовый  | OR? Побитовый | OR сможет при сравнении двух битов выдать необходимую логику. Изначально quiz1 инициализируется нулем, а записывать будем нули и единицы. Значит XOR тоже подходит, ведь два бита со значением 1 отсутствуют.
побитовый | OR сравнивает два значения:
00000000000000000000000000000000
00000100000000000000000000000000
Результат: | OR
00000100000000000000000000000000
Результат: & AND
00000000000000000000000000000000
Результат: ^ XOR
00000100000000000000000000000000

Если необходимо инвертировать бит, простого побитового NOT не достаточно. Надо его доставить в определенную позицию. Например ученик не согласен с оценкой и, вооружившись ключами, проник в учительскую, чтобы своею твердой рукой выразить несогласие с системой. Но хитрый учитель все предусмотрел и файла в экселе нет, а есть quiz1 и побитовые операторы. Чтобы ученик смог инвертировать бит в определенной позиции, нужно сделать следующее:
~(1UL << 27)
сначала доставляет 1 в позицию 27, а затем инвертирует все биты:
11111011111111111111111111111111
а затем в объект quiz1 его вписать с помощью побитового & AND, который на выходе дает 1 только когда оба 1, во всех остальных случаях 0.
quiz1 & ~(1UL << 27)
00000100000000000000000000000000
11111011111111111111111111111111
Следовательно во всех позициях quiz1 будут нули.
Побитовый & AND хорошо справится с задачей, даже если у других учеников будут какие-то оценки, то их не затронет. Ведь он сравнивает с литералом, забитым единицами, а AND изменит только то значение, на месте которого стоит 0, то есть в нашем случае на месте значения соответствующего ученику 27.
Так же есть возможность состояние определенного бита передать переменной типа bool.
bool status=quiz1 & (1UL<<27);
Переменная status получает значение 1, если результат отличен от 0 и 0 если нулевой.
#include <iostream>
int main()
{
unsigned long quiz1=0;
bool status=quiz1 & (1UL << 27);
std::cout << status;
return(0);
}
Вот программа для проверки, и вывод её 0. Что это значит - это значит что если применить побитовый AND и в случае если одно значение будет 0 а другое 1, то получается 0, в случае если биты разные 1 получится только с побитовым | OR. Хотя в книге написано не так. может это я чего не понимаю.

Порядок операторов сдвига левосторонний. Приоритет средний, что означает: ниже чем у арифметических операторов, но выше чем у операторов отношения, присвоения и условных операторов.

4.25.
Каково значение выражения ~'q' << 6 на машине с 32-битовыми целыми числами и 8-битовыми символами, с учетом, что символ 'q' имеет битовое представление 01110001?

Инвертирую 01110001 и получается 10001110 и сдвигаю на 6 бит влево.
Было 00000000000000000000000001110001,
стало после инвертирования 11111111111111111111111111 10001110,
и сдвиг: 1111111111111111111110001110000000

4.26. Что будет, если в приведенном выше примере оценки учеников использовать для переменной quiz1 тип unsigned int?
На разных машинах типы unsigned int и unsigned long могут содержать разное количество бит. А по существу вопроса в моем случае - не изменится ничего. Ибо 32 бита и там и там.

4.27. Каков результат каждого из этих выражений?
unsigned long long ull=3, ul2=7;
(a) ull & ul2
(b) ull | ul2
(c) ull && ul2
(d) ull || ul2

(a) ull & ul2 // побитовое & AND
(b) ull | ul2 // побитовое & OR
(c) ull && ul2 // логическое AND
(d) ull || ul2 // логическое OR
Какой результат?
3 в двоичном 11
7 в двоичном 111
ull1=62 нуля и 2 единицы
ull2=61 нуль и 3 единицы
3 это не нуль и 7 это не нуль, значит в логических операциях сойдут за единицу.
Следовательно:
(a) ull & ul2 // побитовое & AND ответом будет битовый набор ull1
(b) ull | ul2 // побитовое & OR ответом будет битовый набор ull2
(c) ull && ul2 // логическое AND ответом будет значение 1
(d) ull || ul2 // логическое OR ответом будет значение 1
unsigned long long на моей машине  - 64 бита.
Программа для проверки.
#include <iostream>
#include <bitset>
int main()
{
std::cout << sizeof(unsigned long long)*8<< std::endl;
std::bitset<64> ull1 (3);
std::bitset<64> ull2 (7);
std::cout << "& AND" << std::endl;
std::cout << ull1 << std::endl;
std::cout << ull2<< std::endl;
std::bitset<64> ull3=ull1 & ull2;
std::cout << ull3 << std::endl;
std::cout << std::endl;
std::cout << "& OR" << std::endl;
std::cout << ull1 << std::endl;
std::cout << ull2<< std::endl;
std::bitset<64> ull4=ull1 | ull2;
std::cout << ull4 << std::endl;
unsigned long long ull5=3, ull6=7;
bool status=ull5&&ull6;
std::cout << "3&&7=";
std::cout << status << std::endl;
bool status1=ull5&&ull6;
std::cout << "3||7=";
std::cout << status1 << std::endl;
return(0);
}

В программу можно еще для наглядности вставить кусок с побитовым ^ XOR.
std::cout << "^ XOR" << std::endl;
std::cout << ull1 << std::endl;
std::cout << ull2<< std::endl;
std::bitset<64> ull7=ull1 ^ ull2;
std::cout << ull7 << std::endl;

Добавить комментарий