В языке C# предусмотрен поразрядные операторы, расширяющих круг задач, для решения которых можно применять C#. Они применяются для решения самых разных задач программирования на уровне системы, включая, например, анализ информации о состоянии устройства.
Поразрядные операторы воздействуют на отдельные двоичные разряды (биты) своих операндов. Они определены только для целочисленных операндов, поэтому их нельзя применять к данным типа bool, float или double.
Эти операторы называются поразрядными, поскольку они служат для проверки, установки или сдвига двоичных разрядов, составляющих целое значение.
Все доступные в C# поразрядные операторы приведены ниже:
Оператор | Значение |
& | Поразрядное И |
| | Поразрядное ИЛИ |
^ | Поразрядное исключающее ИЛИ |
<< | Сдвиг влево |
>> | Сдвиг вправо |
~ | Дополнение до 1 (унарный оператор НЕ) |
Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ.
Поразрядные операторы выполняют те же функции, что и их логические аналоги. Но в отличие от логических операторов, поразрядные операторы действуют на уровне отдельных двоичных разрядов.
С точки зрения наиболее распространенного применения поразрядную операцию И можно рассматривать как способ подавления отдельных двоичных разрядов. Это означает, что если какой-нибудь бит в любом из операндов равен 0, то соответствующий бит результата будет сброшен в 0.
Поразрядный оператор ИЛИ может быть использован для установки отдельных двоичных разрядов. Если в 1 установлен какой-нибудь бит в любом из операндов этого оператора, то в 1 будет установлен и соответствующий бит в другом операнде.
Поразрядный оператор Исключающее ИЛИ устанавливает двоичный разряд операнда в том и только в том случае, если двоичные разряды сравниваемых операндов оказываются разными, как в приведенном ниже примере.
Унарный оператор НЕ заменяет в каждом разряде 1 на 0, а 0 на 1.
Для понимания вышесказанного, разберите следующие примеры:
1101 0011 1101 0011 1101 0011 1001 1001 1001 1001 1001 1001 1101 0011 &--------- |--------- ^--------- ~--------- 1001 0001 1101 1011 0100 1010 0010 1100
Пример программы, использующей поразрядные операторы:
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { chet(16); provChet(8); nechet(16); Console.ReadLine(); } // Метод, преобразующий все нечетные числа в четные в // диапазоне [0, x] c помощью поразрядного оператора & static void chet(int x) { int result; Console.WriteLine("Преобразованный диапазон чисел от 0 до {0}:\n", x); for (int i = 0; i <= x; i++) { // Сбрасываем младший разряд числа, // чтобы получить четное число result = i & 0xFFFE; Console.Write("{0}\t", result); } } // Метод, проверяющий является ли число четным static void provChet(int x) { Console.WriteLine("\n\nПроверка четности чисел в диапазоне от 1 до {0}\n", x); for (int i = 1; i <= x; i++) { if ((i & 1) == 0) Console.WriteLine("Число {0} - является четным", i); else Console.WriteLine("Число {0} - является нечетным", i); } } // Метод, преобразующий четные числа в нечетные // с помощью поразрядного оператора | static void nechet(int x) { int result; Console.WriteLine("\nПреобразованный диапазон чисел от 0 до {0}:\n", x); for (int i = 0; i <= x; i++) { result = i | 1; Console.Write("{0}\t", result); } } } }
Результат:
Операторы сдвига
В C# имеется возможность сдвигать двоичные разряды, составляющие целое значение, влево или вправо на заданную величину.
Ниже приведена общая форма для этих операторов:
значение << число_битов;
значение >> число_ битов;
где число_битов — это число двоичных разрядов, на которое сдвигается указанное значение.
При сдвиге влево все двоичные разряды в указываемом значении сдвигаются на одну позицию влево, а младший разряд сбрасывается в нуль.
При сдвиге вправо все двоичные разряды в указываемом значении сдвигаются на одну позицию вправо. Если вправо сдвигается целое значение без знака, то старший разряд сбрасывается в нуль. А если вправо сдвигается целое значение со знаком, то разряд знака сохраняется.
Напомним, что для представления отрицательных чисел старший разряд целого числа устанавливается в 1.
Так, если сдвигаемое значение является отрицательным, то при каждом сдвиге вправо старший разряд числа устанавливается в 1.
А если сдвигаемое значение является положительным, то при каждом сдвиге вправо старший разряд числа сбрасывается в нуль.
При сдвиге влево и вправо крайние двоичные разряды теряются. Восстановить потерянные при сдвиге двоичные разряды нельзя, поскольку сдвиг в данном случае не является циклическим.
Рассмотрим пример:
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { byte n = 6, result; // Умножить на 2 result = (byte)(n << 1); Console.WriteLine("{0} * 2 = {1}", n, result); // Умножить на 4 result = (byte)(n << 2); Console.WriteLine("{0} * 4 = {1}", n, result); // Разделить на 2 result = (byte)(n >> 1); Console.WriteLine("{0} / 2 = {1}", n, result); Console.ReadLine(); } } }
Результат:
Рассмотрим далее достаточно замысловатый тернарный (тройной) оператор, заимствованный еще из языка С, или точнее — из Алгола 60.
NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.
что мне нужно подучить что бы понять что тут происходит?
// Сбрасываем младший разряд числа,
// чтобы получить четное число
result = i & 0xFFFE;<—— что это?
Console.Write("{0}\t", result);
и что происходит тут?
/ Сбрасываем младший разряд числа,
/if ((i & 1) == 0)
Console.WriteLine("Число {0} — является четным", i);
Подучить: системы счисления (2-ичную и 16-чную) и булеву алгебру (таблицу истинности для логического И):
Целое число i будет четным, если нулевой двоичный разряд (крайний правый) будет = 0, и нечетным, если 1.
Константа 0xFFFE (16)=1111 1111 1111 1110 (2).
Поэтому поразрядное логическое И делает result всегда четным.
В операторе if константа 1 = 0000 0000 0000 0001. Поэтому i&1 даст либо 0, если i — четное, либо 1, если i — нечетное.
А дальше — проверка на равенство.
В школе помню проходили логические операции и всякие трюки с двоичными и 16ми системами счисления, интересные были задачки)
Скажите, а как часто на практике в разработке прибегают к двоичным системам счислениям? Да и главное есть ли в этом какая-то реальная потребность или из-за привычки/удобства?
Примеры: IP-адрес — четырехбайтное число, Вы можете в нем выделить адрес сети, подсети и компьютера в ней. Представление цвета : 4 байта (альфа-канал и три цвета), преобразования, фильтры. Логика, операции со множествами. И еще много чего, даже целые числа хранятся в двоичной системе счисления. Наверно, эти знания полезны были при программировании на языках низкого уровня.
Пока не вспомнил представление десятичных чисел в двоичной системе,
не понимал о чем речь идет вообще.
А из примера ниже все понятно:
6 в двоичной системе это 110.
сдвигаем влево ряд с прибавлением 0 на конце получается 1100, а это
число 12 наше привычное.
далее если еще раз сдвинуть, будет 11000, это число 24.
другими словами, сдвиг влево это умножение на 2, сдвиг вправо это деление на 2.
Вроде так?