Арифметические операторы
Возвращают результат конкретной арифметической операции.
Вид оператора | Синтаксис | Перегружаемый | Образец сигнатуры (для класса 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)
- Если один из операндов имеет перечисляемый тип с областью действия, никакого преобразования не выполняется: другой операнд и возвращаемое значение должны иметь тот же тип
- В противном случае, если один из операндов longdouble, другой операнд преобразуется в longdouble
- В противном случае, если один из операндов double, другой операнд преобразуется в double
- В противном случае, если один из операндов float, другой операнд преобразуется в float
- В противном случае, операнд имеет целый тип (поскольку типы bool, char, char16_t, char32_t, wchar_t и перечисляемый тип без области действия были расширены) и для получения общего типа применены целочисленные преобразования, а именно:
- Если оба операнда без знака или оба со знаком, операнд с меньшим рангом преобразования преобразуется в операнд с большим рангом преобразования целых типов
- В противном случае, если ранг преобразования операнда без знака больше или равен рангу преобразования операнда со знаком, операнд со знаком преобразуется к типу операнда без знака
- В противном случае, если тип операнда со знаком может представить все значения типа операнда без знака, операнд без знака преобразуется к типу операнда со знаком
- В противном случае оба операнда преобразуется к типу операнда без знака
Ранг преобразования растет в порядке bool, signedchar, short, int, long, longlong. Ранг любого типа без знака равен рангу соответствующего типа со знаком. Ранг char равен рангу signedchar и unsignedchar. Ранги char16_t, char32_t, и wchar_t равны рангам их подлежащих типов.
[править]Переполнения
Арифметика для типов без знака всегда выполняется по модулю 2n
, где n-число битов в данном целом числе. Например, для unsignedint прибавление единицы к 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;unsignedchar n2 =1;unsignedint 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;unsignedint 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;unsignedint 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;unsignedlonglong 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 intlonglong 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 по Арифметические операторы |