Использование битовых операций
Пример 6. Включить четвёртый справа бит, если их нумерация с нуля справа налево. Получить в этом бите единицу или единицу оставить, если она там была, а значения остальных бит не должны измениться.
Первый этап такого упражнения — решить его на битовом уровне, то есть на языке “нулей и единиц”. Пусть работаем с двумя байтами. Тогда условие обозначим так:
· · · · · · · · · · · * · · · ·
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
· · · · · · · · · · · 1 · · · ·
Здесь символом · отмечен бит со значением нуль или единица, значение которого не должно меняться, символом * указан изменяемый бит. Символы "?" означают, какую операцию (& или | или ^), с каким числом (0000000000010000 или 1111111111101111) надо выполнить для решения задачи. Проанализируем все шесть вариантов, или некоторые из них:
1) Операция & c числом 0000000000010000 не подходит. Все биты, кроме четвёртого, обнулим, а четвёртый бит не изменится.
2) Не устроит нас и операция & со вторым числом 1111111111101111. В этом случае наоборот, все биты, кроме четвёртого, останутся без изменения, а в четвёртом бите всегда получится нуль.
3) — 6) аналогично для остальных операций.
Получим
· · · · · · · · · · · * · · · ·
|
0 0 0 0 0 0 0 0 0 0 010 0 0 0
· · · · · · · · · · · 1 · · · ·
Операция битовое или с нулём не меняет значения бита (0 | 0 = 0; 1 | 0 = 1). В результате операции или с единицей всегда получится единица независимо от того, что было на этом месте в исходном числе.
Второй этап: что запишем в тексте программы? Так как в языке С++ типы char и int совместимы (см. дальше в этой главе), то можно работать с одним (char), двумя (short) или четырьмя (int) байтами. Выберем двухбайтный вариант. Программа получается предельно короткой:
short c; cin >> c; c |=16; // 1
printf ("\n%d %X", c, c);
В строке //1 использовали число 16, так как нам надо включить четвёртый бит, а 1610=100002
Третий этап: как проверить такую небольшую программу? Введём любое число с нулевым значением отмеченного “звёздочкой” бита. Например, введём 5, так как 510 = 000001012 содержит нуль в четвёртом бите. Получаем
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1
|
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1
Поэтому выведем числа 21 15, так как 00000000000101012=1516=2110. Можно ввести, например, и число 3610=00000000001001002. Вместо строки // 1 можно также записать: с=с | 16; или с |= 0x10; где 0x10 — шестнадцатеричная константа, так как 00000000000100002=1016=1*16=1610.
Пример 7. Выключить отмеченный символом * бит, то есть в этом разряде получить 0. Значения остальных бит должны остаться без изменения.
Проанализируем, как и в упражнении 1, несколько вариантов. Операция битовое и с единицей не меняет значения бита (0&1=0; 1&1=1), а с нулём всегда даст нуль независимо от того, что было на месте “звёздочки”. Получаем
· · · * · · · ·
&
1 1 1 0 1 1 1 1
· · · 0 · · · ·
В программе пусть char c=0x32;
Проинициализировали переменную значением с единицей на месте отмеченного “звёздочкой” бита. Выполним c &= – 17; printf (“ %5d”, c);
Здесь 1 1 1 0 1 1 1 1 — представление отрицательного числа –17.
0 0 1 1 0 0 1 0
&
1 1 1 0 1 1 1 1
0 0 1 0 0 0 1 0
Получим 2216=2*16+2=3410. и это число выведем.
Пример 8. Заменить k–й справа бит на противоположный, не меняя значения остальных бит.
Ответ: char c=10; short k; cin>>k;
c=c ^ (1<<k); printf("\n%d", c);
Самостоятельно объяснить результат.
Пример 9.Проверить, что находится в 3–м справа бите, нуль или единица.
Ответ: if ((c & 8) == 8) cout<< "1"; else cout<<"0";
Пример 10. Используя битовые операции, вывести двоичное представление двухбайтного целого числа.
Выполняем следующие действия:
1) Получаем значение последнего (правого) бита: с=number & 1.
2) Для получения значения предпоследнего бита сдвигаем число на один разряд вправо и используем ту же битовую операцию & c единицей.
3) Если результат сдвига не запоминали, то сдвигаем число на два разряда вправо и применяем операцию & c единицей.
4) Сдвигаем число на три разряда вправо и применяем ту же операцию.
5). Сдвигаем число на четыре, пять и т.д., на пятнадцать разрядов вправо и применяем ту же операцию & c единицей.
Предложенный алгоритм программируем так:
void Out2 ( unsigned short number, short y0)
{ /* y0 — номер строки окна вывода */
short x=30; /* координата x окна вывода */
for (int j=0; j<16; j++)
{ gotoxy(x--, y0); cout<<(number>>j & 1);
/* Получили одну двоичную цифру 0 или 1 и вывели её, перемешаясь каждый раз влево .*/ }
}
int main() { unsigned short a, y=1;
while (1) { gotoxy(1, y); cin>>a;
if (a==0) return 0; Out2(a, y++); }
}
В функции Out2 в заголовке цикла j<16, так как тип unsigned short определяет целое число с объёмом памяти 2 байта или 16 бит.
В приведенном варианте значение переменной number не изменилось. Если бы результат сдвига запоминали, то на каждом шаге надо сдвигать всегда на один разряд: number= number>> 1; или number >>= 1; и cout<<number & 1; Но в этом варианте значение number в функцииизменится. Но так как в заголовке функции переменная number объявлена без ссылочного типа, то это изменение не передаётся в main, и величина a не изменится.
С помощью приведенной функции можно выводить и двоичное представление отрицательных чисел.
Можно усложнить программу таким образом, чтобы для положительных чисел незначащие нули слева не выводились. В качестве упражнения предлагается внести соответствующие изменения.