From cppreference.com
Many binary operators that expect operands of
or
type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions.
Definition
Usual arithmetic conversions are defined as follows:
Stage 1
Applies
to both operands, the resulting prvalues are used in place of the original operands for the remaining process.
Stage 2
If either operand is of
, no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.
Otherwise, proceed to the next stage.
(since C++11)Stage 3
If either operand is of
, and the other operand is of a different enumeration type or a floating-point type, the expression is ill-formed.
Otherwise, proceed to the next stage.
(since C++26)Stage 4
If either operand is of
, the following rules are applied:
If both operands have the same type, no further conversion will be performed.
Otherwise, if one of the operands is of a non-floating-point type, that operand is converted to the type of the other operand.
Otherwise, if the
floating-point conversion ranks
of the types of the operands are ordered but(since C++23) not equal, then the operand of the type with the lesser floating-point conversion rank is converted to the type of the other operand.
Otherwise, if the floating-point conversion ranks of the types of the operands are equal, then the operand with the lesser
floating-point conversion subrank
is converted to the type of the other operand.
Otherwise, the expression is ill-formed.
(since C++23)Otherwise, both operands are of integer types, proceed to the next stage.
Stage 5
Both operands are converted to a common type C. Given the types T1 and T2 as the promoted type (
under the rules of integral promotions
) of the operands, the following rules are applied to determine C:
If T1 and T2 are the same type, C is that type.
Otherwise, if T1 and T2 are both signed integer types or both unsigned integer types, C is the type of greater
.
Otherwise, one type between T1 and T2 is an signed integer type S, the other type is an unsigned integer type U. Apply the following rules:
If the integer conversion rank of U is greater than or equal to the integer conversion rank of S, C is U.
Otherwise, if S can represent all of the values of U, C is S.
Otherwise, C is the unsigned integer type corresponding to S.
If one operand is of enumeration type and the other operand is of a different enumeration type or a floating-point type, this behavior is deprecated.
(since C++20)
(until C++26)Integer conversion rank
Every
has an integer conversion rank defined as follows:
No two signed integer types other than char and signedchar (if char is signed) have the same rank, even if they have the same representation.
The rank of a signed integer type is greater than the rank of any signed integer type with a smaller width.
The ranks of the following integer types decrease in order:
longlong
(since C++11)long
int
short
signedchar
The rank of any unsigned integer type equals the rank of the corresponding signed integer type.
The rank of any standard integer type is greater than the rank of any extended integer type with the same width.
(since C++11)The rank of bool is less than the rank of all standard integer types.
The ranks of encoding character types (char, char8_t(since C++20), char16_t, char32_t,(since C++11) and wchar_t) equal the ranks of their
, which means:
The rank of char equals the rank of signedchar and unsignedchar.
The rank of char8_t equals the rank of unsignedchar.
(since C++20)The rank of char16_t equals the rank of
.
The rank of char32_t equals the rank of
.
(since C++11)The rank of wchar_t equals the rank of its implementation-defined underlying type.
The rank of any extended signed integer type relative to another extended signed integer type with the same width is implementation-defined, but still subject to the other rules for determining the integer conversion rank.
(since C++11)For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has greater rank than T3, then T1 has greater rank than T3.
The integer conversion rank is also used in the definition of
.
Floating-point conversion rank and subrank
Floating-point conversion rank
Every
has a floating-point conversion rank defined as follows:
The ranks of the standard floating-point types decrease in order: longdouble
double
float
The rank of a floating-point type T is greater than the rank of any floating-point type whose set of values is a proper subset of the set of values of T.
Two extended floating-point types with the same set of values have equal ranks.
An extended floating-point type with the same set of values as exactly one cv-unqualified standard floating-point type has a rank equal to the rank of that standard floating-point type.
An extended floating-point type with the same set of values as more than one cv-unqualified standard floating-point type has a rank equal to the rank of double.
(since C++23)Floating-point conversion subrank
Floating-point types that have equal floating-point conversion ranks are ordered by floating-point conversion subrank. The subrank forms a total order among types with equal ranks.
The types std::float16_t, std::float32_t, std::float64_t, and std::float128_t (
fixed width floating-point types
) have a greater conversion subrank than any standard floating-point type with equal conversion rank. Otherwise, the conversion subrank order is implementation-defined.
(since C++23)Usage
The floating-point conversion rank and subrank are also used to
determine whether a conversion between different floating-point types
or is a
,
distinguish the conversion sequences
in overload resolution,
determine the actual type extracted by
for the extraction of an extended floating-point type using
std::basic_istream::operator>>
,
determinte the actual type inserted by
for the insertion of an extended floating-point type using
std::basic_ostream::operator<<
,
(since C++23)determine whether
's
is explicit, or
determine the common floating-point type if the arguments of different floating-point types are passed to
or
math functions.
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR Applied to Behavior as published Correct behavior
C++98 usual arithmetic conversions might involve lvalues applies lvalue-to-rvalue conversions first
C++20 the three-way comparison between unsignedchar
and unsignedint is ill-formed because
of the intermediate integral promotion
determines the common type based
on the promoted types, without
actually promoting the operands
C++98 when both operands are of the same
floating-point type, the meaning of “no
further conversion is needed” was unclear changed to “no further
conversion will be performed”
Before the resolution, unsignedchar is promoted to int at the beginning of stage 5, then it is converted to unsignedint. However, the latter conversion is narrowing, which makes the three-way comparison ill-formed.
After the resolution, the common type is still unsignedint. The difference is that unsignedchar is directly converted to unsignedint without the intermediate integral promotion. The conversion is not narrowing and hence the three-way comparison is well-formed.