
3.4. Выражения с арифметическими операциями
Кроме операций инкремента и декремента для целочисленных операндов определены, во-первых, стандартные арифметические операции: унарные - и + (определяют знак выражения); бинарные -, +, *, / (вычитание, сложение, умножение, деление).
Среди них заслуживает пояснения только операция деления - ее результат при двух целочисленных операндах всегда округляется до наименьшего по абсолютной величине целого значения.
// 03_01.cs - целочисленное деление
static void Main()
{
Console.WriteLine("-13/4 = " + -13 / 4);
Console.WriteLine("15/-4 = " + 15 /-4);
Console.WriteLine("3/5 = " + 3 / 5);
}
Обратите внимание на аргумент метода WriteLine(). Это выражение с операцией конкатенации строк +. Первая строка – изображение некоторого выражения. Вторая строка – строковое представление результата вычисления арифметического выражения, например - 13/4.
Результат выполнения программы:
-13/4 = -3
15/-4 = -3
3/5 = 0
Отдельно следует рассмотреть операцию получения остатка от деления целочисленных операндов %. При ненулевом делителе для целочисленных величин выполняется соотношение:
(х / у * у +х % у) равно х.
Иллюстрация:
// 03_02.cs - получение остатка от деления
static void Main()
{
Console.WriteLine("13 % 4 = " + 13 % 4);
Console.WriteLine("-13 % 4 = " + -13 % 4);
Console.WriteLine("13 % -4 = " + 13 % -4);
Console.WriteLine("-13 % -4 = " + -13 % -4);
Console.WriteLine("-13/-4*-4 + -13%-4 = " +(-13 / -4 * -4 + -13 % -4));
}
Результат выполнения программы:
13 % 4 = 1
-13 % 4 = -1
13 % -4=1
-13 % -4 = -1
13/-4*-4 + -13%-4 = -13
3.5. Поразрядные операции
От языков Си и Си++ язык С# унаследовал операции для работы с битовыми представлениями целых чисел:
~ - поразрядное инвертирование (поразрядное НЕТ); &- поразрядная конъюнкция (поразрядное И);
| - поразрядная дизъюнкция (поразрядное ИЛИ);
^ - взаимоисключающее поразрядное ИЛИ;
>> - поразрядный сдвиг вправо;
<< - поразрядный сдвиг влево.
Таблица 3.1. Правила выполнения поразрядных операций
Значения операндов |
Результаты выполнения операции | |||
Первого |
Второго |
& |
| |
^ |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
Для иллюстрации выполнения поразрядных операций удобно использовать операнды беззнакового байтового типа (byte). Рассмотрим вначале поразрядную унарную операцию инвертирования (~). Операция применяется к каждому разряду (биту) внутреннего представления целочисленного операнда. Предположим, что значение переменной bb беззнакового байтового типа равно 3. Внутреннее представление bb имеет вид 00000011. После выполнения операции ~ битовым представлением результата (т.е. выражения ~bb), станет 11111100, то есть 252 при использовании десятичного основания.
Операция &. Определим две байтовых переменных bb со значением 3 и dd со значением 6:
byte bb=3, dd=6;
Поразрядные (битовые) представления: 00000011 и 00000110. Значением выражения bb&dd будет десятичное 2, имеющее внутреннее представление 00000010.
Применив к переменным bb и dd бинарную операцию поразрядной дизъюнкции |, получим десятичное значение 7 с поразрядным представлением 00000111.
Применив бинарную операцию ^ исключающего ИЛИ к переменным bb и dd получим десятичное значение 5 с битовым представлением 00000101.
Следующая программа содержит описанные выше выражения с поразрядными операциями над переменными типа byte.
//03_03.cs - поразрядные операции с беззнаковыми переменными
static void Main03()
{
byte bb = 3;
Console.WriteLine("bb = " + bb + "; ~bb = " + (byte)(~bb));
byte dd = 6;
Console.WriteLine("bb & dd = " + (byte)(bb & dd));
Console.WriteLine("bb | dd = " + (byte)(bb | dd));
Console.WriteLine("bb ^ dd = " + (byte)(bb ^ dd));
}
Поясним еще раз особенности обращений к методу WriteLine(). Аргумент метода должен быть строкового типа (типа string). Выражение вида
строка + арифметическое выражение
обрабатывается так: вычисляется арифметическое выражение, его значение автоматически преобразуется в строку, которая "присоединяется" к строке, размещенной слева от знака +. Чтобы значение арифметического выражения при преобразованиях "не потеряло" свой беззнаковый тип, явно используется операция приведения типов (byte). Необходимость указанного приведения типов будет обоснована в § 3.7. Более подробно приведение типов рассмотрено в следующей главе.
Результаты выполнения программы:
bb = 3; ~bb = 252
bb&dd = 2
bb | dd = 7
bb ^ dd = 5
Бинарные поразрядные операции сдвига (>> и <<) по-разному используют значения своих операндов. Левый операнд задает целочисленное значение, к битовому представлению которого применяется сдвиг. Правый операнд указывает количество разрядов (битов), на которое должны сдвигаться все биты внутреннего представления левого операнда. Направление сдвига зависит от операции: << обозначает сдвиг влево, >> обеспечивает сдвиг вправо.
При сдвиге влево << все разряды, выходящие за левый край внутреннего представления значения левого операнда, отбрасываются, все "освободившиеся" справа позиции заполняются нулями. Таким образом, сдвинув влево на 3 позиции число 4 с двоичным представлением 00000100, получим 00100000 (десятичное 32). Сдвиг влево числа 00010000 (десятичное 16) на 4 позиции приводит к нулевому значению. Обратим внимание, что сдвиг влево на к позиций эквивалентен умножению левого операнда на 2к. (При условии, что значащие левые ненулевые разряды не выйдут за разрядную сетку.) Значением выражения 6<<3 будет десятичное число 48, то есть 6*23 или 00110000.
При сдвиге вправо >> разряды, выходящие за правый край представления значения левого операнда, отбрасываются. Слева "освободившиеся" позиции заполняются нулями. Таким образом, число 25 с двоичным представлением 00011001 при сдвиге на 2 позиции вправо приводит к получению кода 00000110 со значением 6. Сдвиг вправо на к позиций эквивалентен умножению на 2к с округлением результата до целого значения. Выражение 6>>2 будет равно 1, то есть 00000001.
Следующая программа иллюстрирует сказанное относительно поразрядных сдвигов:
//03_04.cs - операции сдвигов для беззнаковых целых
static void Main04()
{
byte bb = 4;
Console.WriteLine("bb = " + bb + ", bb << 3 = " + (byte)(bb << 3));
bb = 16;
Console.WriteLine("bb = " + bb + ", bb << 4 = " + (byte)(bb << 4));
bb = 6;
Console.WriteLine("bb = " + bb + ", bb << 3 = " + (byte)(bb << 3));
bb = 25;
Console.WriteLine("bb = " + bb + ", bb >> 2 = " + (byte)(bb >> 2));
bb = 6;
Console.WriteLine("bb = " + bb + ", bb >> 2 = " + (byte)(bb >> 2));
}
Результат выполнения программы:
bb = 4; bb << 3 = 32
bb = 16; bb << 4 = 0
bb = 6; bb << 3 = 48
bb = 25; bb >> 2 = 6
bb = 6; bb >> 2 = 1