Арифметические операторы
Возвращают результат конкретной арифметической операции.
Вид оператора | Синтаксис | Перегружаемый | Образец сигнатуры (для класса T) | |
---|---|---|---|---|
Функция-член класса | Свободная функция | |||
унарный + | +a
|
Да | T T::operator+() const; | T operator+(const T &a); |
унарный - | -a
|
Да | T T::operator-() const; | T operator-(const T &a); |
сложение | a + b
|
Да | T T::operator+(const T2 &b) const; | T operator+(const T &a, const T2 &b); |
вычитание | a - b
|
Да | T T::operator-(const T2 &b) const; | T operator-(const T &a, const T2 &b); |
умножение | a * b
|
Да | T T::operator*(const T2 &b) const; | T operator*(const T &a, const T2 &b); |
деление | a / b
|
Да | T T::operator/(const T2 &b) const; | T operator/(const T &a, const T2 &b); |
взятие остатка | a % b
|
Да | T T::operator%(const T2 &b) const; | T operator%(const T &a, const T2 &b); |
побитовое НЕ | ~a
|
Да | T T::operator~() const; | T operator~(const T &a); |
побитовое И | a & b
|
Да | T T::operator&(const T2 &b) const; | T operator&(const T &a, const T2 &b); |
побитовое ИЛИ | a | b
|
Да | T T::operator|(const T2 &b) const; | T operator|(const T &a, const T2 &b); |
побитовое исключающее ИЛИ | a ^ b
|
Да | T T::operator^(const T2 &b) const; | T operator^(const T &a, const T2 &b); |
сдвиг влево | a << b
|
Да | T T::operator<<(const T2 &b) const; | T operator<<(const T &a, const T2 &b); |
сдвиг вправо | a >> b
|
Да | T T::operator>>(const T2 &b) const; | T operator>>(const T &a, const T2 &b); |
|
Содержание |
[править] Объяснение
Все арифметические операции вычисляют результат конкретного арифметического действия и возвращают его результат. Аргументы не изменяются.
[править] Преобразования
Если операнд, переданный арифметической операции, целочисленного типа или перечисляемого типа без области действия, то перед любым другим действием (но после преобразования l-value в r-value преобразования, если это применимо) операнд подвергается целочисленному расширению. Если операнд является массивом или функцией, то применяется преобразование массива к указателю или функции к указателю.
Для бинарных операций (кроме операций сдвига), если операнды после расширения имеют разные типы, применяются дополнительные неявные преобразования, известные как обычные арифметические преобразования с целью получения общего типа (также может быть определен через свойство типа std::common_type). Если один из операндов имеет перечисляемый тип (до применения расширения), а другой имеет тип с плавающей запятой или перечисляемый тип, отличный от типа первого операнда, данное поведение не рекомендовано. (начиная с C++20)
- Если один из операндов имеет перечисляемый тип с областью действия, никакого преобразования не выполняется: другой операнд и возвращаемое значение должны иметь тот же тип
- В противном случае, если один из операндов long double, другой операнд преобразуется в long double
- В противном случае, если один из операндов double, другой операнд преобразуется в double
- В противном случае, если один из операндов float, другой операнд преобразуется в float
- В противном случае, операнд имеет целый тип (поскольку типы bool, char, char16_t, char32_t, wchar_t и перечисляемый тип без области действия были расширены) и для получения общего типа применены целочисленные преобразования, а именно:
- Если оба операнда без знака или оба со знаком, операнд с меньшим рангом преобразования преобразуется в операнд с большим рангом преобразования целых типов
- В противном случае, если ранг преобразования операнда без знака больше или равен рангу преобразования операнда со знаком, операнд со знаком преобразуется к типу операнда без знака
- В противном случае, если тип операнда со знаком может представить все значения типа операнда без знака, операнд без знака преобразуется к типу операнда со знаком
- В противном случае оба операнда преобразуется к типу операнда без знака
Ранг преобразования растет в порядке bool, signed char, short, int, long, long long. Ранг любого типа без знака равен рангу соответствующего типа со знаком. Ранг char равен рангу signed char и unsigned char. Ранги char16_t, char32_t, и wchar_t равны рангам их подлежащих типов.
[править] Переполнения
Арифметика для типов без знака всегда выполняется по модулю 2n
, где n-число битов в данном целом числе. Например, для unsigned int прибавление единицы к UINT_MAX даст 0, а вычитание единицы из 0 даст UINT_MAX.
Когда арифметическая операция над целым числом со знаком приводит к переполнению (значение результата не умещается в типе результата), то поведение не определено и возможны варианты:
- циклический возврат результата к нулю (wrap-around) в соответствии с правилами представления целых чисел (как правило, представимых в дополнительном коде)
- генерация исключения (зависит от платформы или опций компилятора - например,
-ftrapv
в GCC и Clang)
- насыщение - ограничение результата минимальным или максимальным значением (на многих DSP-процессорах)
- операция может быть полностью удалена компилятором при оптимизации.
[править] Окружение для операций с плавающей точкой
Если pragma-опция
#pragma STDC FENV_ACCESS поддерживается и установлена в ON
, то все арифметические операции с плавающей точкой должны соблюдать текущее направление округления rounding direction и сигнализировать об ошибках в соответствии с math_errhandling, кроме вычислений в ходе статической инициализации (static initializer), когда исключения не возбуждаются и округление происходит до ближайшего представимого значения.
[править] Свёртывание операций с плавающей точкой
Кроме случая, когда pragma-опция
#pragma STDC FP_CONTRACT поддерживается и установлена в OFF
, все арифметические операции с плавающей точкой могут быть выполнены, как если бы промежуточные результаты имели бесконечные диапазон представления и точность, то есть разрешены оптимизации, пренебрегающие ошибками округления или исключениями для таких операций. Например, стандарт C++ разрешает реализацию вычисления (x*y) + z в виде одной инструкции совмещённого умножения-сложения или оптимизацию a = x*x*x*x; в виде tmp = x *x; a = tmp*tmp.
Вне зависимости от свёртывания промежуточные результаты арифметических операций с плавающей точкой могут иметь диапазон представления и точность отличные от тех, которые указаны типом (см. FLT_EVAL_METHOD).
Формально, стандарт C++ не даёт гарантий на точность операций с плавающей точкой.
[править] Унарные арифметические операции
Выражения для унарных арифметических операций имеют форму:
+ выражение
|
(1) | ||||||||
- выражение
|
(2) | ||||||||
унарный +
(целочисленное расширение).Встроенная операция унарный + возвращает значение своего операнда. Единственная ситуация, при которой данная операция не является пустой операцией, это когда операнд имеет целочисленный тип или перечисляемый тип без области действия и тип был изменен расширением, например, оно преобразовало char в int или, если операнд подлежит преобразованию l-value к r-value, массива к указателю, или функции к указателю.
Встроенная операция унарный - вычисляет величину своего операнда с противоположным знаком. Для беззнакового a, значение -a равно 2b
-a, где b - число битов после расширения.
Для каждого расширенного арифметического типа A
и для каждого типа T
в разрешении перегрузки в отношении данных операций, определённых пользователем принимают участие следующие сигнатуры функций:
A operator+(A) |
||
T* operator+(T*) |
||
A operator-(A) |
||
#include <iostream> int main() { char c = 0x6a; int n1 = 1; unsigned char n2 = 1; unsigned int n3 = 1; std::cout << "char: " << c << " int: " << +c << '\n' << "-1, where 1 is signed: " << -n1 << '\n' << "-1, where 1 is unsigned char: " << -n2 << '\n' << "-1, where 1 is unsigned int: " << -n3 << '\n'; char a[3]; std::cout << "size of array: " << sizeof a << '\n' << "size of pointer: " << sizeof +a << '\n'; }
Вывод:
char: j int: 106 -1, where 1 is signed: -1 -1, where 1 is unsigned char: -1 -1, where 1 is unsigned int: 4294967295 size of array: 3 size of pointer: 8
[править] Аддитивные операции
Выражения для бинарной аддитивной арифметической операции имеют форму
lhs + rhs
|
(1) | ||||||||
lhs - rhs
|
(2) | ||||||||
- оба должны иметь арифметический тип или тип перечисления без области действия. В этом случае выполняются обычные арифметические преобразования обоих операндов, определяющие тип результата
- или один из операндов должен быть указателем на полный объектный тип, а второй иметь целочисленный тип или тип перечисления без области действия. В этом случае тип результата будет иметь тип указателя
- оба должны иметь арифметический тип или тип перечисления без области действия. В этом случае выполняются обычные арифметические преобразования обоих операндов, определяющие тип результата
- lhs должен быть указателем на полный объектный тип, а rhs иметь целочисленный тип или тип перечисления без области действия. В этом случае тип результата будет иметь тип указателя
- оба должны быть указателями на один и тот же полный объектный тип, игнорируя cv-квалификаторы. В этом случае std::ptrdiff_t будет типом результата
Если операнды имеют арифметический тип или тип перечисления, то результатом бинарной операции + будет сумма обоих операндов (после обычных арифметических преобразований), а результатом бинарной операции - будет разность первого и второго операндов (после обычных арифметических преобразований), за исключением случая, когда тип поддерживает IEEE-арифметику с плавающей точкой (см. std::numeric_limits::is_iec559),
- если один из операндов NaN, то результатом будет NaN
- (∞ - ∞) = NaN и возбуждается исключение FE_INVALID
- (∞ + ∞) = NaN и возбуждается исключение FE_INVALID
Если любой из операндов является указателем, то применяются следующие правила:
- Указатель на объект не-массив рассматривается как указатель на первый элемент некоторого массива размера 1
- Если указатель
P
указывает наi
-й элемент массива, то выраженияP+n
,n+P
, иP-n
являются указателями того же типа, что и указатели на(i+n)
-й,(n+i)
-й, и(i-n)
-й элемент того же массива соответственно. Результатом прибавления к указателю целого числа может быть указатель на позицию за последним элементом (то есть такой указательP
, что выражениеP-1
указывает на последний элемент массива). Любые другие операции (т.е. попытки получить указатель, который не указывает на элемент того же массива или на позицию за последним элементом) ведут к неопределенному поведению - Если указатель
P
указывает наi
-й элемент массива, а указательQ
указывает наj
-й элемент того же массива, то выражениеP-Q
принимает значение i-j, если это значение умещается в тип std::ptrdiff_t. Оба операнда должны указывать на элементы одного и того же массива (или на позицию за последним элементом массива), в противном случае поведение не определено. Если результат не умещается в std::ptrdiff_t, поведение не определено. - Если значение 0 добавляется или вычитается из указателя, то результатом является тот же указатель. Если два указателя указывают на один и тот же объект или на позицию за последним элементом в том же массиве, или оба указателя нулевые, то результат вычитания равен (std::ptrdiff_t)0
Указатели в данных арифметических операциях удовлетворяют концепту RandomAccessIterator
Для каждой пары расширенных арифметических типов L
и R
и для каждого объекта типа T
, в разрешении перегрузки в отношении данных операций, определённых пользователем принимают участие следующие сигнатуры функций:
LR operator+(L, R) |
||
LR operator-(L, R) |
||
T* operator+(T*, std::ptrdiff_t) |
||
T* operator+(std::ptrdiff_t, T*) |
||
T* operator-(T*, std::ptrdiff_t) |
||
std::ptrdiff_t operator-(T*, T*) |
||
,где LR
является результатом обычных арифметических преобразований L
и R
#include <iostream> int main() { char c = 2; unsigned int un = 2; int n = -10; std::cout << " 2 + (-10), where 2 is a char = " << c + n << '\n' << " 2 + (-10), where 2 is unsigned = " << un + n << '\n' << " -10 - 2.12 = " << n - 2.12 << '\n'; char a[4] = {'a', 'b', 'c', 'd'}; char* p = &a[1]; std::cout << "Pointer addition examples: " << *p << *(p + 2) << *(2 + p) << *(p - 1) << '\n'; char* p2 = &a[4]; std::cout << "Pointer difference: " << p2 - p << '\n'; }
Вывод:
2 + (-10), where 2 is a char = -8 2 + (-10), where 2 is unsigned = 4294967288 -10 - 2.12 = -12.12 Pointer addition examples: bdda Pointer difference: 3
[править] Мультипликативные операции
Выражения для бинарной мультипликативной арифметической операции имеют форму:
lhs * rhs
|
(1) | ||||||||
lhs / rhs
|
(2) | ||||||||
lhs % rhs
|
(3) | ||||||||
Бинарная операция * выполняет умножение своих операндов (после обычных арифметических преобразований), за исключением случая умножения с плавающей точкой, при котором
- результатом умножения NaN на любое число будет NaN
- результатом умножения бесконечности на 0 будет NaN и возбуждается исключение FE_INVALID
Бинарная операция / делит первый операнд на второй (после обычных арифметических преобразований). Для целочисленных операндов результатом будет алгебраическое частное.
Частное округляется в направлении, определяемом реализацией. |
(до C++11) |
Частное округляется в направлении 0 (дробная часть отбрасывается). |
(начиная с C++11) |
Если делитель (второй операнд) равен 0, то поведение не определено, за исключением деления с плавающей точкой, когда тип поддерживает IEEE-арифметику с плавающей точкой (см. std::numeric_limits::is_iec559), тогда:
- если операнд равен NaN, результатом будет NaN
- результатом деления числа, не равного 0, на ±0.0 будет бесконечность со знаком и возбуждается исключение FE_DIVBYZERO
- результатом деления 0.0 на 0.0 будет NaN и возбуждается исключение FE_INVALID
Бинарная операция % возвращает остаток целочисленного деления первого операнда на второй (после обычных целочисленных преобразований). Оба операнда должны иметь целый тип. Если значение частного a/b представимо в типе результата, то (a/b)*b + a%b == a. Если второй операнд равен 0, то поведение не определено. Если значение частного a/b не представимо в тип результата, то поведение операций a/b и a%b не определено (результат INT_MIN%-1 не определён для чисел в дополнительном коде).
Примечание: До стандарта C++11, если один или оба операнда были отрицательными, знак остатка определялся реализацией, поскольку он зависит от направления округления при целочисленном делении. Для обеспечения переносимости следует применять функцию std::div, поведение которой определено.
Примечание: Для вычисления остатка от деления чисел с плавающей точкой см. std::remainder и std::fmod.
Для каждой пары расширенных арифметических типов LA
и RA
и для каждой пары расширенных целых типов LI
и RI
в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
LRA operator*(LA, RA) |
||
LRA operator/(LA, RA) |
||
LRI operator%(LI, RI) |
||
,где LRx
является результатом обычных арифметических преобразований Lx
и Rx
#include <iostream> int main() { char c = 2; unsigned int un = 2; int n = -10; std::cout << "2 * (-10), where 2 is a char = " << c * n << '\n' << "2 * (-10), where 2 is unsigned = " << un * n << '\n' << "-10 / 2.12 = " << n / 2.12 << '\n' << "-10 / 21 = " << n / 21 << '\n' << "-10 % 21 = " << n % 21 << '\n'; }
Вывод:
2 * (-10), where 2 is a char = -20 2 * (-10), where 2 is unsigned = 4294967276 -10 / 2.12 = -4.71698 -10 / 21 = 0 -10 % 21 = -10
[править] Побитовые операции
Выражения для побитовых арифметических операций имеют форму:
~ rhs
|
(1) | ||||||||
lhs & rhs
|
(2) | ||||||||
lhs | rhs
|
(3) | ||||||||
lhs ^ rhs
|
(4) | ||||||||
Результатом операции ~ является побитовое НЕ (в обратном коде) значения аргумента (после расширения). Результатом операции & является значение побитового И значений операндов (после обычных арифметических преобразований). Результатом операции | является значение побитового ИЛИ значений операндов (после обычных арифметических преобразований). Результатом операции ^ является значение побитового исключающего ИЛИ (XOR) значений операндов (после обычных арифметических преобразований).
Для каждой пары расширенных целых типов L
и R
в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
R operator~(R) |
||
LR operator&(L, R) |
||
LR operator^(L, R) |
||
LR operator|(L, R) |
||
,где LR
является результатом обычных арифметических преобразований L
и R
#include <iostream> int main() { std::cout << std::hex << std::showbase; uint16_t mask = 0x00f0; uint32_t a = 0x12345678; std::cout << "Value: " << a << " mask: " << mask << '\n' << "Setting bits: " << (a | mask) << '\n' << "Clearing bits: " << (a & ~mask) << '\n' << "Selecting bits: " << (a & mask) << '\n'; }
Вывод:
Value: 0x12345678 mask: 0xf0 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x70
[править] Операции сдвига
Выражения для операций сдвига имеют форму:
lhs << rhs
|
(1) | ||||||||
lhs >> rhs
|
(2) | ||||||||
Возвращаемым типом является тип левого операнда после целочисленных расширений.
Для беззнакового a значение a << b есть остаток от деления a * 2b
Для отрицательного a поведение a << b не определено. Для беззнакового a и неотрицательного b со знаком, значение a >> b является целой частью a/2b Для отрицательного a, значение a >> b определяется реализацией (в большинстве реализаций выполняется арифметический сдвиг вправо так, что результат остается отрицательным). |
(до C++20) |
Значение a << b единственно и сравнимо с a * 2b Значение a >> b есть a/2b |
(начиная с C++20) |
В любом случае, если значение правого операнда является отрицательным или больше или равно количеству бит расширенного левого операнда, то поведение не определено.
Для каждой пары расширенных целых типов L
и R
в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
L operator<<(L, R) |
||
L operator>>(L, R) |
||
#include <iostream> enum {ONE=1, TWO=2}; int main() { std::cout << std::hex << std::showbase; char c = 0x10; unsigned long long ull = 0x123; std::cout << "0x123 << 1 = " << (ull << 1) << '\n' << "0x123 << 63 = " << (ull << 63) << '\n' // overflow in unsigned << "0x10 << 10 = " << (c << 10) << '\n'; // char is promoted to int long long ll = -1000; std::cout << std::dec << "-1000 >> 1 = " << (ll >> ONE) << '\n'; }
Вывод:
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
[править] Стандартная библиотека
Арифметические операции перегружены для многих типов, определённых в стандартной библиотеке.
[править] Унарные арифметические операции
реализует унарный + и унарный - (public функция-элемент std::chrono::duration<Rep,Period> )
| |
применяет унарные операторы к комплексным числам (шаблон функции) | |
применяют унарные арифметические операции к каждому элементу valarray (public функция-элемент std::valarray )
|
[править] Аддитивные операции
(C++11) |
выполняет операции сложения и вычитания, связанные с моментом времени (шаблон функции) |
реализует арифметические операции с duration в качестве аргументов (шаблон функции) | |
объединяет две строки или строку и символ (шаблон функции) | |
продвигает итератор вперед или назад (public функция-элемент std::reverse_iterator )
| |
продвигает итератор вперед или назад (public функция-элемент std::move_iterator )
| |
выполняет арифметику комплексных чисел над двумя комплексными значениями или комплексным и скалярным значениями (шаблон функции) | |
применяют бинарные операторы к каждому элементу двух valarray или valarray и значению (шаблон функции) |
[править] Мультипликативные операции
реализует арифметические операции с duration в качестве аргументов (шаблон функции) | |
выполняет арифметику комплексных чисел над двумя комплексными значениями или комплексным и скалярным значениями (шаблон функции) | |
применяют бинарные операторы к каждому элементу двух valarray или valarray и з��ачению (шаблон функции) |
[править] Побитовые операции
выполняет бинарное И, ИЛИ, исключающее ИЛИ и НЕ (public функция-элемент std::bitset<N> )
| |
выполняют бинарные логические операции над наборами битов (шаблон функции) | |
применяет логическую операцию НЕ к каждому элементу std::valarray (public функция-элемент std::valarray )
| |
применяют побитовые операции И, ИЛИ, исключающее ИЛИ к элементам двух std::valarray попарно, или к элементам std::valarray и значению (шаблон функции) |
[править] Операции сдвига
применяют операции сдвига влево/вправо к элементам двух std::valarray попарно, или к элементам std::valarray и значению (шаблон функции) | |
выполняют двоичный сдвиг влево/вправо элементов std::bitset (public функция-элемент std::bitset<N> )
|
[править] Операции вставки / извлечения из потока
Обычно в стандартной библиотеке операции сдвига перегружены для класса потока ввода/вывода (std::ios_base& или классов, производных от него), как для типа левого операнда так и для возвращаемого типа. Такие операции известны как вставка в поток и извлечение из потока:
извлекает форматированные данные из потока ввода std::basic_istream (public функция-элемент std::basic_istream )
| |
извлекает форматированные данные из потока ввода std::basic_istream (шаблон функции) | |
вставляет форматированные данные в поток вывода std::basic_ostream (public функция-элемент std::basic_ostream )
| |
вставляет символьные данные в поток вывода std::basic_ostream (функция) | |
сериализует и десериализует комплексное число (шаблон функции) | |
выполняют потоковый ввод и вывод наборов битов (шаблон функции) | |
выполняет потоковый ввод и вывод для строк (шаблон функции) | |
выполняют потоковый вывод/ввод для объекта генератора псевдослучайных чисел (функция) | |
выполняют потоковый вывод/ввод для объекта распределения псевдослучайных чисел |
[править] См. также
Общие операторы | ||||||
---|---|---|---|---|---|---|
присваивание | инкремент декремент |
арифметические | логические | сравнения | доступ к элементу | другие |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
вызов функции |
a(...) | ||||||
запятая | ||||||
a, b | ||||||
условный | ||||||
a ? b : c | ||||||
Специальные операторы | ||||||
static_cast приводит один тип к другому совместимому типу |
Документация C по Арифметические операторы
|