Namespaces
Variants

Arithmetic operators

From cppreference.net

Арифметические операторы применяют стандартные математические операции к своим операндам.

Оператор Название оператора Пример Результат
+ унарный плюс + a значение a после продвижений
- унарный минус - a отрицание a
+ сложение a + b сумма a и b
- вычитание a - b разность a и b
* умножение a * b произведение a и b
/ деление a / b частное от деления a на b
% остаток от деления a % b остаток от деления a на b
~ побитовое НЕ ~a побитовое НЕ a
& побитовое И a & b побитовое И a и b
| побитовое ИЛИ a | b побитовое ИЛИ a и b
^ побитовое исключающее ИЛИ a ^ b побитовое исключающее ИЛИ a и b
<< побитовый сдвиг влево a << b a сдвинутый влево на b
>> побитовый сдвиг вправо a >> b a сдвинутый вправо на b

Содержание

Переполнения

Арифметика беззнаковых целых чисел всегда выполняется по модулю 2 n
, где n — количество битов в данном целом числе. Например, для unsigned int , добавление единицы к UINT_MAX даёт 0 , а вычитание единицы из 0 даёт UINT_MAX .

Когда происходит переполнение при выполнении арифметической операции со знаковыми целыми числами (результат не помещается в тип результата), поведение не определено: оно может осуществлять перенос в соответствии с правилами представления (обычно дополнительный код), может вызывать ловушку на некоторых платформах или из-за параметров компилятора (например, -ftrapv в GCC и Clang), или может быть полностью оптимизировано компилятором .

Среда чисел с плавающей запятой

Если #pragma STDC FENV_ACCESS установлена в значение ON , все операторы арифметики с плавающей точкой подчиняются текущему направлению округления и сообщают об ошибках арифметики с плавающей точкой, как указано в math_errhandling , за исключением случаев, когда они являются частью статической инициализации (в этом случае исключения с плавающей точкой не вызываются, а режим округления установлен на ближайшее значение)

Сокращение операций с плавающей точкой

Если #pragma STDC FP_CONTRACT не установлена в значение OFF , вся арифметика с плавающей точкой может выполняться так, как если бы промежуточные результаты имели бесконечный диапазон и точность, то есть допускаются оптимизации, исключающие ошибки округления и исключения с плавающей точкой, которые наблюдались бы при точном вычислении выражения в соответствии с записью. Например, позволяет реализовать ( x * y ) + z с помощью одной инструкции FMA процессора или оптимизировать a = x * x * x * x ; в tmp = x * x ; a = tmp * tmp .

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

Унарная арифметика

Унарные арифметические операторы имеют следующий вид

+ выражение (1)
- выражение (2)
1) унарный плюс (продвижение типа)
2) унарный минус (отрицание)
expression - выражение любого arithmetic type

Унарный плюс и унарный минус сначала применяют integral promotions к своему операнду, а затем

  • унарный плюс возвращает значение после продвижения
  • унарный минус возвращает отрицание значения после продвижения (за исключением того, что отрицание NaN является другим NaN)

Тип выражения - это тип после продвижения, а категория значения является не-lvalue.

Примечания

Унарный минус вызывает неопределённое поведение из-за переполнения знакового целого числа при применении к INT_MIN , LONG_MIN или LLONG_MIN на типичных (дополнение до 2) платформах.

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

#include <stdio.h>
#include <complex.h>
#include <limits.h>
int main(void)
{
    char c = 'a';
    printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c);
    printf("-1, where 1 is signed: %d\n", -1);
    // Определенное поведение, поскольку арифметика выполняется для беззнакового целого.
    // Следовательно, вычисление (-1) по модулю (2 в степени n) = UINT_MAX, где n -
    // количество бит unsigned int. Если unsigned int имеет длину 32 бита, то это
    // дает (-1) по модулю (2 в степени 32) = 4294967295
    printf("-1, where 1 is unsigned: %u\n", -1u); 
    // Неопределенное поведение, потому что математическое значение -INT_MIN = INT_MAX + 1
    // (т.е. на 1 больше максимального возможного значения для знакового int)
    //
    // printf("%d\n", -INT_MIN);
    // Неопределенное поведение, потому что математическое значение -LONG_MIN = LONG_MAX + 1
    // (т.е. на 1 больше максимального возможного значения для знакового long)
    //
    // printf("%ld\n", -LONG_MIN);
    // Неопределенное поведение, потому что математическое значение -LLONG_MIN = LLONG_MAX + 1
    // (т.е. на 1 больше максимального возможного значения для знакового long long)
    //
    // printf("%lld\n", -LLONG_MIN);
    double complex z = 1 + 2*I;
    printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z));
}

Возможный вывод:

sizeof char: 1 sizeof int: 4
-1, where 1 is signed: -1
-1, where 1 is unsigned: 4294967295
-(1+2i) = -1.0-2.0

Аддитивные операторы

Выражения бинарного арифметического оператора сложения имеют вид

lhs + rhs (1)
lhs - rhs (2)
1) сложение: lhs и rhs должны быть одним из следующих вариантов
  • оба имеют арифметические типы , включая комплексные и мнимые
  • один является указателем на полный объектный тип, другой имеет целочисленный тип
2) вычитание: lhs и rhs должны быть одним из следующих вариантов
  • оба имеют арифметические типы , включая комплексные и мнимые
  • lhs имеет указатель на полный объектный тип, rhs имеет целочисленный тип
  • оба являются указателями на полные объекты совместимых типов, без учёта квалификаторов

Арифметическое сложение и вычитание

Если оба операнда имеют арифметические типы , тогда

  • сначала usual arithmetic conversions выполняются
  • затем значения операндов после преобразований складываются или вычитаются согласно обычным математическим правилам (для вычитания rhs вычитается из lhs ), за исключением того, что
  • если один операнд — NaN, результат — NaN
  • бесконечность минус бесконечность дает NaN и FE_INVALID возбуждается
  • бесконечность плюс отрицательная бесконечность дает NaN и FE_INVALID возбуждается

Сложение и вычитание комплексных и мнимых чисел определяется следующим образом (обратите внимание, что тип результата является мнимым, если оба операнда мнимые, и комплексным, если один операнд вещественный, а другой мнимый, как указано обычными арифметическими преобразованиями):

+ или - u iv u + iv
x x ± u x ± iv (x ± u) ± iv
iy ±u + iy i(y ± v) ±u + i(y ± v)
x + iy (x ± u) + iy x + i(y ± v) (x ± u) + i(y ± v)


// work in progress
// note: take part of the c/language/conversion example

Арифметика указателей

  • Если указатель P указывает на элемент массива с индексом I , тогда
  • P + N и N + P являются указателями, которые указывают на элемент того же массива с индексом I+N
  • P - N является указателем, который указывает на элемент того же массива с индексом I-N

Поведение определено только в том случае, если и исходный указатель, и результирующий указатель указывают на элементы одного массива или на позицию за последним элементом этого массива. Обратите внимание, что выполнение p-1, когда p указывает на первый элемент массива, является неопределенным поведением и может завершиться ошибкой на некоторых платформах.

  • Если указатель P1 указывает на элемент массива с индексом I (или на позицию за последним элементом) и P2 указывает на элемент того же массива с индексом J (или на позицию за последним элементом), тогда
  • P1 - P2 имеет значение, равное I - J и тип ptrdiff_t (который является знаковым целочисленным типом, обычно вдвое меньшим размера самого большого объекта, который может быть объявлен)

Поведение определено только в случае, если результат помещается в ptrdiff_t .

Для целей арифметики указателей, указатель на объект, не являющийся элементом какого-либо массива, рассматривается как указатель на первый элемент массива размера 1.

// work in progress
int n = 4, m = 3;
int a[n][m];     // VLA of 4 VLAs of 3 ints each
int (*p)[m] = a; // p == &a[0] 
p = p + 1;       // p == &a[1] (pointer arithmetic works with VLAs just the same)
(*p)[2] = 99;    // changes a[1][2]

Мультипликативные операторы

Бинарные выражения арифметических операторов умножения имеют вид

lhs * rhs (1)
lhs / rhs (2)
lhs % rhs (3)
**Примечание:** В данном случае весь текст на странице состоит из C++ специфических терминов (`lhs`, `rhs`) и математических операторов (`*`, `/`, `%`), которые согласно инструкциям не подлежат переводу. Числовые обозначения (1), (2), (3) также сохраняются в оригинальном виде. HTML структура полностью сохранена без изменений.
1) умножение. lhs и rhs должны иметь арифметические типы
2) деление. lhs и rhs должны иметь арифметические типы
3) остаток. lhs и rhs должны иметь целочисленные типы

Умножение

Бинарный оператор * выполняет умножение своих операндов (после обычных арифметических преобразований) в соответствии с обычными арифметическими определениями, за исключением того, что

  • если один операнд является NaN, результат - NaN
  • умножение бесконечности на ноль дает NaN и FE_INVALID возбуждается
  • умножение бесконечности на ненулевое значение дает бесконечность (даже для комплексных аргументов)

Поскольку в C любое комплексное значение с хотя бы одной бесконечной частью является бесконечностью, даже если его другая часть является NaN, обычные арифметические правила не применяются к умножению комплексных чисел. Другие комбинации операндов с плавающей точкой следуют следующей таблице:

* u iv u + iv
x xu i(xv) (xu) + i(xv)
iy i(yu) −yv (−yv) + i(yu)
x + iy (xu) + i(yu) (−yv) + i(xv) специальные правила

Помимо обработки бесконечностей, комплексное умножение не допускает переполнения промежуточных результатов, за исключением случаев, когда #pragma STDC CX_LIMITED_RANGE установлено в значение ON , в этом случае значение может вычисляться как если бы по формуле (x+iy)×(u+iv) = (xu-yv)+i(yu+xv) , так как программист берёт на себя ответственность за ограничение диапазона операндов и работу с бесконечностями.

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

#include <stdio.h>
#include <stdio.h>
#include <complex.h>
#include <math.h>
int main(void)
{
// TODO более простые случаи, взять некоторые из C++
   double complex z = (1 + 0*I) * (INFINITY + I*INFINITY);
// по учебной формуле получилось бы
// (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN
// но C дает комплексную бесконечность
   printf("%f + i*%f\n", creal(z), cimag(z));
// по учебной формуле получилось бы
// cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN
// но C дает  ±∞+i*nan
   double complex y = cexp(INFINITY + I*NAN);
   printf("%f + i*%f\n", creal(y), cimag(y));
}

Возможный вывод:

inf + i*inf 
inf + i*nan

Деление

Бинарный оператор / делит первый операнд на второй (после обычных арифметических преобразований) в соответствии с обычными арифметическими определениями, за исключением того, что

  • когда тип после обычных арифметических преобразований является целочисленным, результат представляет собой алгебраическое частное (не дробь), округлённое в определённом реализацией направлении (до C99) усечённое в сторону нуля (начиная с C99)
  • если один операнд является NaN, результат - NaN
  • если первый операнд представляет комплексную бесконечность, а второй операнд конечен, то результат операции / представляет собой комплексную бесконечность
  • если первый операнд конечен, а второй операнд представляет комплексную бесконечность, то результат операции / равен нулю.

Поскольку в C любое комплексное значение с хотя бы одной бесконечной частью считается бесконечностью, даже если другая его часть является NaN, обычные арифметические правила не применяются к делению комплексных чисел. Другие комбинации операндов с плавающей точкой следуют приведённой ниже таблице:

/ u iv
x x/u i(−x/v)
iy i(y/u) y/v
x + iy (x/u) + i(y/u) (y/v) + i(−x/v)

Помимо обработки бесконечностей, комплексное деление не должно вызывать переполнение промежуточных результатов, за исключением случаев, когда #pragma STDC CX_LIMITED_RANGE установлен в значение ON , в этом случае значение может вычисляться как если бы по формуле (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u 2
+v 2
)
, так как программист берёт на себя ответственность за ограничение диапазона операндов и обработку бесконечностей.

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

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

  • Деление ненулевого числа на ±0.0 дает правильно-знаковую бесконечность и FE_DIVBYZERO возбуждается
  • Деление 0.0 на 0.0 дает NaN и FE_INVALID возбуждается

Остаток от деления

Бинарный оператор % возвращает остаток от деления первого операнда на второй (после обычных арифметических преобразований).

Знак остатка определяется таким образом, что если частное a/b представимо в результирующем типе, то ( a / b ) * b + a % b == a .

Если второй операнд равен нулю, поведение не определено.

Если частное a/b не представимо в результирующем типе, поведение как a/b , так и a%b является неопределённым (это означает, что INT_MIN %- 1 не определено в системах с дополнительным кодом)

Примечание: оператор остатка не работает с типами с плавающей точкой, библиотечная функция fmod предоставляет эту функциональность.

Побитовая логика

Выражения с побитовыми арифметическими операторами имеют вид

~ rhs (1)
lhs & rhs (2)
lhs | rhs (3)
lhs ^ rhs (4)
**Примечание:** В данном случае весь текст на странице состоит из C++ специфических терминов (`rhs`, `lhs`) и операторов (`~`, `&`, `|`, `^`), которые согласно инструкциям не подлежат переводу. Числовые обозначения в скобках также сохраняются в оригинальном виде.
1) побитовое НЕ
2) побитовое И
3) побитовое ИЛИ
4) bitwise XOR

где

lhs , rhs - выражения целочисленного типа

Сначала операторы & , ^ и | выполняют обычные арифметические преобразования для обоих операндов, а оператор ~ выполняет целочисленные повышения для своего единственного операнда.

Затем соответствующие бинарные логические операторы применяются побитово; то есть каждый бит результата устанавливается или сбрасывается в соответствии с логической операцией (NOT, AND, OR или XOR), применённой к соответствующим битам операндов.

Примечание: побитовые операторы обычно используются для работы с битовыми наборами и битовыми масками.

Примечание: для беззнаковых типов (после продвижения) выражение ~E эквивалентно максимальному значению, представимому результирующим типом, минус исходное значение E .

#include <stdio.h>
#include <stdint.h>
int main(void)
{
    uint32_t a = 0x12345678;
    uint16_t mask = 0x00f0;
    printf("Promoted mask:\t%#010x\n"
           "Value:\t\t%#x\n"
           "Setting bits:\t%#x\n"
           "Clearing bits:\t%#x\n"
           "Selecting bits:\t%#010x\n"
           , mask
           , a
           , a | mask
           , a & ~mask
           , a & mask
    );
}

Возможный вывод:

Promoted mask:  0x000000f0
Value:          0x12345678
Setting bits:   0x123456f8
Clearing bits:  0x12345608
Selecting bits: 0x00000070

Операторы сдвига

Выражения с операторами побитового сдвига имеют вид

lhs << rhs (1)
lhs >> rhs (2)
**Примечание:** В данном HTML-коде нет текста для перевода, требующего локализации на русский язык. Все элементы содержат либо HTML-теги и атрибуты, либо C++ операторы (`<<`, `>>`), либо технические обозначения (`lhs`, `rhs`), которые согласно инструкциям не подлежат переводу.
1) сдвиг влево lhs на rhs бит(ов)
2) сдвиг вправо lhs на rhs битов

где

lhs , rhs - выражения целочисленного типа

Сначала целочисленные повышения выполняются отдельно для каждого операнда (Примечание: это отличается от других бинарных арифметических операторов, которые выполняют обычные арифметические преобразования). Тип результата соответствует типу lhs после повышения.

Поведение не определено, если rhs отрицателен или больше либо равен количеству битов в продвинутом lhs .

Для беззнакового lhs значение LHS << RHS равно значению LHS * 2 RHS
, приведённому по модулю максимального значения возвращаемого типа плюс 1 (то есть выполняется побитовый сдвиг влево, и биты, выходящие за пределы целевого типа, отбрасываются). Для знакового lhs с неотрицательными значениями значение LHS << RHS равно LHS * 2 RHS
, если оно представимо в продвинутом типе lhs , в противном случае поведение не определено.

Для беззнакового lhs и для знакового lhs с неотрицательными значениями, значение LHS >> RHS является целой частью от LHS / 2 RHS
. Для отрицательного LHS значение LHS >> RHS определяется реализацией, причём в большинстве реализаций выполняется арифметический сдвиг вправо (так что результат остаётся отрицательным). Таким образом, в большинстве реализаций при правом сдвиге знакового LHS новые старшие биты заполняются исходным знаковым битом (т.е. 0, если число было неотрицательным, и 1, если оно было отрицательным).

#include <stdio.h>
enum {ONE=1, TWO=2};
int main(void)
{
    char c = 0x10;
    unsigned long long ulong_num = 0x123;
    printf("0x123 << 1  = %#llx\n"
           "0x123 << 63 = %#llx\n"   // переполнение обрезает старшие биты для беззнаковых чисел
           "0x10  << 10 = %#x\n",    // char продвигается до int
           ulong_num << 1, ulong_num << 63, c << 10);
    long long long_num = -1000;
    printf("-1000 >> 1 = %lld\n", long_num >> ONE);  // определяется реализацией
}

Возможный вывод:

0x123 << 1  = 0x246
0x123 << 63 = 0x8000000000000000
0x10  << 10 = 0x4000
-1000 >> 1 = -500

Ссылки

  • Стандарт C17 (ISO/IEC 9899:2018):
  • 6.5.3.3 Унарные арифметические операторы (стр. 64)
  • 6.5.5 Мультипликативные операторы (стр. 66)
  • 6.5.6 Аддитивные операторы (стр. 66-68)
  • 6.5.7 Операторы побитового сдвига (стр. 68)
  • 6.5.10 Оператор побитового И (стр. 70)
  • 6.5.11 Оператор побитового исключающего ИЛИ (стр. 70)
  • 6.5.12 Оператор побитового включающего ИЛИ (стр. 70-71)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.5.3.3 Унарные арифметические операторы (стр. 89)
  • 6.5.5 Мультипликативные операторы (стр. 92)
  • 6.5.6 Аддитивные операторы (стр. 92-94)
  • 6.5.7 Операторы битового сдвига (стр. 94-95)
  • 6.5.10 Оператор побитового И (стр. 97)
  • 6.5.11 Оператор побитового исключающего ИЛИ (стр. 98)
  • 6.5.12 Оператор побитового включающего ИЛИ (стр. 98)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.5.3.3 Унарные арифметические операторы (стр. 79)
  • 6.5.5 Мультипликативные операторы (стр. 82)
  • 6.5.6 Аддитивные операторы (стр. 82-84)
  • 6.5.7 Операторы битового сдвига (стр. 84-85)
  • 6.5.10 Оператор побитового И (стр. 87)
  • 6.5.11 Оператор побитового исключающего ИЛИ (стр. 88)
  • 6.5.12 Оператор побитового включающего ИЛИ (стр. 88)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 3.3.3.3 Унарные арифметические операторы
  • 3.3.5 Мультипликативные операторы
  • 3.3.6 Аддитивные операторы
  • 3.3.7 Операторы побитового сдвига
  • 3.3.10 Оператор побитового И
  • 3.3.11 Оператор побитового исключающего ИЛИ
  • 3.3.12 Оператор побитового включающего ИЛИ

Смотрите также

Приоритет операторов

Общие операторы
присваивание инкремент
декремент
арифметические логические сравнения доступа к членам
доступа
другие

a = b
a + = b
a - = b
a * = b
a / = b
a % = b
a & = b
a | = b
a ^ = b
a <<= b
a >>= b

++ a
-- a
a ++
a --

+ a
- a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

! a
a && b
a || b

a == b
a ! = b
a < b
a > b
a <= b
a >= b

a [ b ]
* a
& a
a - > b
a. b

a ( ... )
a, b
( type ) a
a ? b : c
sizeof


_Alignof
(начиная с C11)
(до C23)

alignof
(начиная с C23)

Документация C++ для Арифметические операторы