VirQ. Побитовые операции &, |, ^, ~ над числом в C#
Язык C# входит в ТОП-5
самых популярных языков
программирования
Главная / Целочисленные переменные /
Побитовые операции &, |, ^, ~ над числом
Статей на сайте: 61

Побитовые операции &, |, ^, ~ над числом


Первое, что необходимо понимать, это то, что побитовые операции выполняются с числами в двоичной системе счисления.

Оператор &

[code] int a = 117 & 61; //Результат: 53 [/code]
Разложим 117 и 61 в виде двоичной системы и запишем цифры друг под другом, выравниваясь к правому краю:
[code] 1110101 //117 0111101 //61 ─────── оператор & 0110101 //53 [/code]

Оператор & получит биты =1 только там, где у обоих чисел в этой позиции бита стоит цифра 1. Получаем 0110101, а это и есть число 53 в 10-ичной системе.

Оператор |

[code] int a = 117 & 61; //Результат: 125 [/code]
Записываем столбик:
[code] 1110101 //117 0111101 //61 ─────── оператор | 1111101 //125 [/code]
Оператор | получит биты =1 только там, где хотя бы у одного из чисел в этом бите стоит цифра 1. Получаем 1111101, а это и есть число 125 в 10-ичной системе.

Оператор ^

[code] int a = 117 ^ 61; //Результат: 72 [/code]
Записываем столбик:
[code] 1110101 //117 0111101 //61 ─────── оператор ^ 1001000 //72 [/code]
Оператор ^ получит биты 1 только там, где биты РАЗНЫЕ в этом числе. Получили 1001000. А это и есть 72 в 10-тичной системе

Оператор ~

Это оператор изменения всех битов числа с 0 на 1 и наоборот. Оператор унарный. Это означает, что он работает не с парой чисел, а только с одним числом.
Если было число 43 = 101011 в 2-ичной системе, то такой код:
[code] short n = 43; short x = ~n; //Результат: -44 [/code]
выдаст в ответ -44.
Объяснить и понять это несколько сложнее, чем предыдущие 3 примера с бинарными операциями &, |, ^.
Все числа, например, типа short, до 32767 переводятся в 2-ичную систему обычно. И
3276710 = 0111 1111 1111 11112
Однако дальше идёт так:
-3276810 = 1000 0000 0000 00002
-3276710 = 1000 0000 0000 00012
-3276610 = 1000 0000 0000 00102
-3276510 = 1000 0000 0000 00112
...
-110 = 1111 1111 1111 11112

Соответственно:
~0 = -1
~1 = -2
~2 = -3
...
~32767 = -32768
т.е. можно рассмотреть так:
~0000 0000 0000 0000 = 1111 1111 1111 1111
~0000 0000 0000 0001 = 1111 1111 1111 1110
~0000 0000 0000 0010 = 1111 1111 1111 1101
~0000 0000 0000 0011 = 1111 1111 1111 1100
~1000 0000 0000 0000 = 0111 1111 1111 1111


В типах uint, ulong знак ~ показывает как раз число с обратной стороны диапазона.
Например, в uint диапазон от 0 до 4294967295, тогда ~5 = 4294967290, а для числа
~1000 будет 4294966295 (т.е. максимальное значение 4294967295 минус 1000)

010 = 0000 0000 0000 00002
...
3276710 = 0111 1111 1111 11112
Здесь всё одинаково. А дальше для беззнаковых типов так:
3276810 = 1000 0000 0000 00002
3276910 = 1000 0000 0000 00012
3277010 = 1000 0000 0000 00102
3277110 = 1000 0000 0000 00112
...
6553510 = 1111 1111 1111 11112

Соответственно:
~0 = 65535
~1 = 65534
~2 = 65533

Однако, надо не забывать, что числа не ограничиваются 16 битами, поэтому надо ограничить самостоятельно, чтобы получить этот результат:
[code] int a = 3; int b = (ushort)~a; //Тут получим 65532 (это на 3 от конца диапазона чисел типа ushort) int e = 3; uint t = (uint)~e; //Тут получим 4294967292(это на 3 от конца диапазона чисел типа uint) int c = 3; int d = ~c; //Если не приводить тип, то получим -4 int x = 1000; uint m = (uint)~x; //Получим 4294966295. Это на 1000 отстоит от конца диапазона [/code]

Вопрос: где это можно применить?
В одной байтовой переменной можно сохранить до 8 различных значений, принимающих Да (1) или Нет (0). Примером такого является перечисление FontStyle. Вот так это выглядит:
[code] public enum FontStyle { Regular = 0, //обычный Bold = 1, //полужирный Italic = 2, //курсивный Underline = 4, //подчёркнутый Strikeout = 8, //зачёркнутый } [/code]
В данном случае, каждое из становится свою ячейку. Происходит это так: например, у нас шрифт полужирный и подчёркнутый. Надо как-то хранить понятие "полужирный" И "подчёркнутый".
Это 1 + 4 = 5
Число 5 в двоичной системе, это 0101 (рассматривать слева направо. Для данного случая: Strikeout = 0, Underline = 1, Italic = 0, Bold = 1)
Если, например, слово будет зачёркнутым, полужирным и курсивным, то это 1011 (1 + 2 + 8 = 11)
Получается, у нас есть 4 бита, спрятанные в 1 числе.
В данном случае, число может принимать до 16 различных комбинаций от 0 до 15. И внутри "самого числа" хранятся сразу 4 переменных. Такой метод позволяет существенно экономить как память, так и скорость получения данных, т.к. побитовые операции гораздо быстрее получения данных из отдельной переменной.

Дополнительная информация о том, как вывести результат в консоли или WinForm