Поделиться через


Арифметические операторы (справочник по C#)

Следующие операторы выполняют арифметические операции с операндами числовых типов:

Все целочисленные и числовые типы с плавающей запятой поддерживают эти операторы.

Типы int, uint, long и ulong определяют все эти операторы. Другие целочисленные типы (sbyte, byte, short, ushort и char) определяют только операторы ++ и --. Для других операторов, если вы используете целочисленные типы sbyte, byteshortushortили char операнды, значения преобразуются в int тип и тип intрезультата. Если операнды являются разными целочисленными или плавающими типами, их значения преобразуются в ближайший содержащий тип, если такой тип существует. Дополнительные сведения см. в разделе Числовые повышения уровня в статье Спецификации языка C#. Операторы ++ и -- определяются для всех целочисленных числовых типов и числовых типов с плавающей запятой, а также типа char. Тип результата выражения сложного назначения является типом левого операнда.

Справочные документы по языку C# описывают последнюю выпущенную версию языка C#. Она также содержит начальную документацию по функциям в общедоступных предварительных версиях для предстоящего языкового выпуска.

Документация определяет любую функцию, впервые представленную в последних трех версиях языка или в текущих общедоступных предварительных версиях.

Подсказка

Чтобы узнать, когда функция впервые появилась в C#, ознакомьтесь со статьей об истории версий языка C#.

Оператор инкремента ++

Оператор инкремента ++ увеличивает операнд на 1. Операндом должна быть переменная, свойство или индексатор.

Оператор инкремента поддерживается в двух формах: постфиксный оператор инкремента (x++) и префиксный оператор инкремента (++x).

Постфиксный оператор приращения

Результатом x++ является значение xперед выполнением операции, как показано в следующем примере:

int i = 3;
Console.WriteLine(i);   // output: 3
Console.WriteLine(i++); // output: 3
Console.WriteLine(i);   // output: 4

Префиксный оператор инкремента

Результатом ++x является значение xпосле выполнения операции, как показано в следующем примере:

double a = 1.5;
Console.WriteLine(a);   // output: 1.5
Console.WriteLine(++a); // output: 2.5
Console.WriteLine(a);   // output: 2.5

Оператор декремента --

Унарный оператор декремента -- уменьшает операнд на 1. Операндом должна быть переменная, свойство или индексатор.

Оператор декремента доступен в двух формах: оператор декремента постфикса, x--а также оператор декремента префикса. --x

Постфиксный оператор уменьшения

Результатом x-- является значение xперед выполнением операции, как показано в следующем примере:

int i = 3;
Console.WriteLine(i);   // output: 3
Console.WriteLine(i--); // output: 3
Console.WriteLine(i);   // output: 2

Префиксный оператор декремента

Результатом --x является значение xпосле выполнения операции, как показано в следующем примере:

double a = 1.5;
Console.WriteLine(a);   // output: 1.5
Console.WriteLine(--a); // output: 0.5
Console.WriteLine(a);   // output: 0.5

Операторы унарного плюса и минуса

Унарный оператор + возвращает значение полученного операнда. Унарный оператор - изменяет знак операнда на противоположный.

Console.WriteLine(+4);     // output: 4

Console.WriteLine(-4);     // output: -4
Console.WriteLine(-(-4));  // output: 4

uint a = 5;
var b = -a;
Console.WriteLine(b);            // output: -5
Console.WriteLine(b.GetType());  // output: System.Int64

Console.WriteLine(-double.NaN);  // output: NaN

Тип ulong не поддерживает унарный оператор -.

Оператор умножения *

Оператор умножения * вычисляет произведение операндов:

Console.WriteLine(5 * 2);         // output: 10
Console.WriteLine(0.5 * 2.5);     // output: 1.25
Console.WriteLine(0.1m * 23.4m);  // output: 2.34

Унарный оператор * представляет собой оператор косвенного обращения к указателю.

Оператор деления /

Оператор деления / делит левый операнд на правый.

Деление целых чисел

Для операндов целочисленных типов результат / оператора имеет целочисленный тип и равен кавычению двух операндов, округленных до нуля:

Console.WriteLine(13 / 5);    // output: 2
Console.WriteLine(-13 / 5);   // output: -2
Console.WriteLine(13 / -5);   // output: -2
Console.WriteLine(-13 / -5);  // output: 2

Чтобы получить кавычкой двух операндов в виде числа с плавающей запятой, используйте float, doubleили decimal тип:

Console.WriteLine(13 / 5.0);       // output: 2.6

int a = 13;
int b = 5;
Console.WriteLine((double)a / b);  // output: 2.6

Деление чисел с плавающей запятой

floatДля типа и doubledecimal типа /оператор возвращает кворент двух операндов:

Console.WriteLine(16.8f / 4.1f);   // output: 4.097561
Console.WriteLine(16.8d / 4.1d);   // output: 4.09756097560976
Console.WriteLine(16.8m / 4.1m);   // output: 4.0975609756097560975609756098

Если один операнд является decimal, другой операнды не могут быть float или double, поскольку ни не floatdouble имеет неявного преобразования decimalв . Необходимо явным образом преобразовать операнд float или double в тип decimal. Дополнительные сведения о числовых преобразованиях см. в разделе Встроенные числовые преобразования.

Оператор остатка %

Оператор остатка % вычисляет остаток от деления левого операнда на правый.

Целочисленный остаток

Для операндов целых типов результатом является значение, a % b созданное $a - \frac{a}{b} \times b$. Знак оставшейся части ненуля соответствует знаку левого операнда, как показано в следующем примере:

Console.WriteLine(5 % 4);   // output: 1
Console.WriteLine(5 % -4);  // output: 1
Console.WriteLine(-5 % 4);  // output: -1
Console.WriteLine(-5 % -4); // output: -1

Используйте метод Math.DivRem для вычисления результатов как целочисленного деления, так и определения остатка.

Остаток с плавающей запятой

Для операндов типа float и double результатом x % y для конечных x и y будет значение z, так что:

  • Знак , если ненулевая, соответствует знаку zx.
  • Абсолютное значение z поступает из вычисления $|x| - n \times |y|$, где n наибольшее целое число меньше или равно $\frac{|x|}{|y|}$. Здесь $|x|$ и $|y|$ представляют абсолютные значения x и yсоответственно.

Примечание.

Этот метод вычисления оставшейся части аналогичен методу, используемому для целых операндов, но отличается от спецификации IEEE 754. Если вам нужна операция вычисления остатка, которая соответствует спецификации IEEE 754, используйте метод Math.IEEERemainder.

Сведения о поведение оператора % в случае неконечных операндов см. в разделе Оператор остаткаспецификации языка C#.

Для decimal операндов остальные операторы % работают так же, как и остальные операторыSystem.Decimal типа.

В следующем примере показано, как работает оставшийся оператор с операндами с плавающей запятой:

Console.WriteLine(-5.2f % 2.0f); // output: -1.2
Console.WriteLine(5.9 % 3.1);    // output: 2.8
Console.WriteLine(5.9m % 3.1m);  // output: 2.8

Оператор сложения +

Оператор сложения + вычисляет сумму своих операндов:

Console.WriteLine(5 + 4);       // output: 9
Console.WriteLine(5 + 4.3);     // output: 9.3
Console.WriteLine(5.1m + 4.2m); // output: 9.3

Кроме того, оператор + можно использовать для объединения строк и делегатов. Дополнительные сведения см. в статье Операторы + и +=.

Оператор вычитания -

Оператор вычитания вычитает - правый операнд из левого операнда:

Console.WriteLine(47 - 3);      // output: 44
Console.WriteLine(5 - 4.3);     // output: 0.7
Console.WriteLine(7.5m - 2.3m); // output: 5.2

Оператор также можно использовать - для удаления делегата. Дополнительные сведения см. в - разделе и -= операторах.

Составное присваивание

Для бинарного оператора op выражение составного присваивания в форме

x op= y

Эквивалентен

x = x op y

За исключением того, что x оценивается только один раз.

Следующий пример иллюстрирует использование составного присваивания с арифметическими операторами:

int a = 5;
a += 9;
Console.WriteLine(a);  // output: 14

a -= 4;
Console.WriteLine(a);  // output: 10

a *= 2;
Console.WriteLine(a);  // output: 20

a /= 4;
Console.WriteLine(a);  // output: 5

a %= 3;
Console.WriteLine(a);  // output: 2

Из-за числовых повышений результат op операции может не быть неявно преобразован в тип Tx. В этом случае, если op является предопределенным оператором, и результат операции является явно преобразуемым в тип Tx, выражение составного присваивания формы x op= y эквивалентно x = (T)(x op y), за исключением того, что x вычисляется только один раз. В следующем примере продемонстрировано такое поведение.

byte a = 200;
byte b = 100;

var c = a + b;
Console.WriteLine(c.GetType());  // output: System.Int32
Console.WriteLine(c);  // output: 300

a += b;
Console.WriteLine(a);  // output: 44

В предыдущем примере значение является результатом преобразования значения 44300 в byte тип.

Примечание.

В контексте проверки переполнения предыдущий Дополнительные сведения см. в разделе арифметического переполнения целочисленного числа.

Вы также можете использовать операторы += и -= для подписки и отмены подписки на события соответственно. Дополнительные сведения см. в разделе Практическое руководство. Подписка и отмена подписки на события.

Очередность и ассоциативность операторов

Следующий список упорядочивает арифметические операторы в группах с наивысшим приоритетом до наименьшего приоритета:

  • (первичные) операторы: добавочные x++ и декрементные x-- операторы.
  • (Унарные) операторы: префикс инкремент ++x и декременты --x , а также унарные + и - операторы.
  • (Умножение) операторов: *, /и % операторов.
  • (Аддитивные) операторы: двоичные + и - операторы.

Бинарные арифметические операторы имеют левую ассоциативность. То есть компилятор вычисляет операторы с одинаковым уровнем приоритета слева направо.

Порядок вычисления, определяемый приоритетом и ассоциативностью операторов, можно изменить с помощью скобок (()).

Console.WriteLine(2 + 2 * 2);   // output: 6
Console.WriteLine((2 + 2) * 2); // output: 8

Console.WriteLine(9 / 5 / 2);   // output: 0
Console.WriteLine(9 / (5 / 2)); // output: 4

Полный список операторов C#, упорядоченный по уровню приоритета, можно найти в разделе Приоритет операторов статьи Операторы C#.

Арифметическое переполнение и деление на нуль

Если результат арифметической операции выходит за пределы диапазона возможных конечных значений соответствующего числового типа, поведение арифметического оператора зависит от типа его операндов.

Целочисленное арифметическое переполнение

Деление целого числа на ноль всегда вызывает исключение DivideByZeroException.

В случае целочисленного арифметического переполнения итоговое поведение определяется проверяемым или непроверяемым контекстом проверки переполнения:

  • Если в проверяемом контексте переполнение возникает в константном выражении, происходит ошибка времени компиляции. В противном случае, если операция производится во время выполнения, возникает исключение OverflowException.
  • В непроверяемом контексте результат усекается путем удаления старших разрядов, которые не помещаются в целевой тип данных.

Примечание.

Целочисленное деление имеет особый случай, когда ArithmeticException можно создать даже в незавершенном контексте. Если левый операнд является минимальным значением целочисленного типа со знаком (int.MinValue или long.MinValue) и правым операндом -1, результат не может быть представлен в целевом типе. Среда выполнения .NET вызывается ArithmeticException в этом случае, как показано в следующем примере:

int a = int.MinValue;
int b = -1;
try
{
    int c = unchecked(a / b);
}
catch (ArithmeticException)
{
    Console.WriteLine($"Overflow occurred when dividing {a} by {b}.");
}

Вместе с проверяемыми и непроверяемыми операторами можно использовать операторы checked и unchecked, чтобы управлять контекстом проверки переполнения, в котором вычисляется выражение:

int a = int.MaxValue;
int b = 3;

Console.WriteLine(unchecked(a + b));  // output: -2147483646
try
{
    int d = checked(a + b);
}
catch(OverflowException)
{
    Console.WriteLine($"Overflow occurred when adding {a} to {b}.");
}

По умолчанию арифметические операции выполняются в непроверяемом контексте.

Арифметическое переполнение с плавающей запятой

Арифметические операции с помощью float и double типов никогда не вызывают исключения. Результат арифметических операций с использованием этих типов может быть одним из специальных значений, представляющих бесконечность и не число:

double a = 1.0 / 0.0;
Console.WriteLine(a);                    // output: Infinity
Console.WriteLine(double.IsInfinity(a)); // output: True

Console.WriteLine(double.MaxValue + double.MaxValue); // output: Infinity

double b = 0.0 / 0.0;
Console.WriteLine(b);                // output: NaN
Console.WriteLine(double.IsNaN(b));  // output: True

Для операндов типа decimal арифметическое переполнение всегда выдает исключение OverflowException, а деление на нуль — DivideByZeroException.

Ошибки округления

Из-за общих ограничений в представлении с плавающей запятой реальных чисел и арифметических с плавающей запятой ошибки округления могут возникать в вычислениях, использующих типы с плавающей запятой. Результат выражения может отличаться от ожидаемого математического результата. В следующем примере показано несколько таких случаев:

Console.WriteLine(.41f % .2f); // output: 0.00999999

double a = 0.1;
double b = 3 * a;
Console.WriteLine(b == 0.3);   // output: False
Console.WriteLine(b - 0.3);    // output: 5.55111512312578E-17

decimal c = 1 / 3.0m;
decimal d = 3 * c;
Console.WriteLine(d == 1.0m);  // output: False
Console.WriteLine(d);          // output: 0.9999999999999999999999999999

Дополнительные сведения см. на страницах справки System.Double, System.Single или System.Decimal .

Возможность перегрузки оператора

Вы можете перегрузить арифметические операторы арифметических арифметических операторов (, ,, и++--) унарных (+-*/%, +-и) для определяемого пользователем типа. При перегрузке двоичного оператора вы также неявно перегружаете соответствующий оператор составного назначения. Начиная с C# 14 определяемый пользователем тип может явно перегружать операторы составных назначений (op=), чтобы обеспечить более эффективную реализацию. Как правило, тип перегружает эти операторы, так как значение может быть обновлено вместо выделения нового экземпляра для хранения результата операции. Если тип не предоставляет явной перегрузки, компилятор создает неявную перегрузку.

Проверенные операторы, определяемые пользователем

При перегрузке арифметического оператора можно использовать checked ключевое слово для определения проверенной версии этого оператора. Следующий пример показывает, как это сделать:

public record struct Point(int X, int Y)
{
    public static Point operator checked +(Point left, Point right)
    {
        checked
        {
            return new Point(left.X + right.X, left.Y + right.Y);
        }
    }
    
    public static Point operator +(Point left, Point right)
    {
        return new Point(left.X + right.X, left.Y + right.Y);
    }
}

При определении проверенного оператора следует также определить соответствующий оператор без модификатора checked. Проверенный контекст вызывает проверяемый оператор, а снятый контекст вызывает оператор без checked модификатора.

При определении обоих версий оператора их поведение отличается только в том случае, если результат операции слишком велик, чтобы представить в типе результата следующим образом:

  • Проверенный оператор вызывает OverflowException.
  • Оператор без модификатора checked возвращает экземпляр, который представляет усеченный результат.

Сведения о разнице в поведении встроенных арифметических операторов см. в разделе Арифметическое переполнение и деление на ноль.

Модификатор checked можно использовать только при перегрузке следующих операторов:

Примечание.

Модификатор checked не влияет на контекст проверки переполнения в его теле. Контекст по умолчанию определяется значением параметра компилятора CheckForOverflowUnderflow. С помощью операторов checked и unchecked можно явно указать контекст проверки переполнения, как показано в примере в начале этого раздела.

Спецификация языка C#

Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:

См. также