Integer Promotions vs Arithmetic Conversions in C++

In C++, implicit type conversions occur automatically during expression evaluation to maintain consistency and match underlying processor architectures. Although standard integer promotions and general arithmetic conversions (formally referred to as "usual arithmetic conversions") are closely related, they represent distinct stages of this process. Standard integer promotions elevate small integral types to int or unsigned int to prevent value truncation and optimize hardware registers. In contrast, usual arithmetic conversions apply to binary expressions containing mixed arithmetic types, determining a single common type so the operation can be evaluated safely.

Standard Integer Promotions

Standard integer promotions apply to small integer types, unscoped enumerations, and bit-fields. Types with an integer conversion rank lower than that of signed int—such as bool, char, signed char, unsigned char, short, and unsigned short—are automatically promoted before an operation occurs.

The promotion rules are straightforward:

  • If int can represent all values of the original type, the value is converted to int.
  • If int cannot represent all values (which can occur with unsigned short on systems where sizeof(short) == sizeof(int)), the value is converted to unsigned int.

Integer promotions can occur on single operands (unary operations such as +, -, or ~) or as an initial phase of a binary operation. For example, adding two char variables causes both to be promoted to int before the addition takes place:

char a = 10;
char b = 20;
auto result = a + b; // Type of 'result' is int, not char

General (Usual) Arithmetic Conversions

Usual arithmetic conversions are a structured set of rules applied to the operands of most binary operators (such as +, -, *, /, <, and ==) when the operands have different arithmetic types. The objective is to find a common type that both operands can be safely converted to before the operation executes.

The conversion follows a strict hierarchy:

  1. Floating-Point Handling: If either operand is a floating-point type, the operand with the lower rank is converted to the type of the operand with the higher rank (e.g., float to double, or double to long double).
  2. Integral Promotion Step: If neither operand is a floating-point type, standard integer promotions are first applied to both operands.
  3. Integral Type Balancing: If the types still differ after promotion, they are adjusted using rank-based rules:
    • If both operands are signed or both are unsigned, the operand with the lower conversion rank is converted to the type of the higher rank (e.g., int becomes long).
    • If the operand with the unsigned type has a rank greater than or equal to the signed operand, the signed operand is converted to the unsigned type.
    • If the signed type can represent all values of the unsigned type, the unsigned operand is converted to the signed type. Otherwise, both operands are converted to the unsigned version of the signed operand's type.

For example, mixing a signed integer with an unsigned integer:

int a = -5;
unsigned int b = 10;
auto result = a + b; // 'a' is converted to unsigned int; result is unsigned int

Key Differences

  • Scope and Applicability: Integer promotions apply to single operands of small integral types, including in unary operations and shift counts. Arithmetic conversions apply strictly to binary expressions with two operands.
  • Pipeline Hierarchy: Integer promotion is a subset and prerequisite of arithmetic conversions. When two integers are operated on, the compiler first promotes any small integers before executing the broader rules of usual arithmetic conversions.
  • Target Type: Integer promotions exclusively convert to either int or unsigned int. Usual arithmetic conversions can yield any arithmetic type, including long long, float, double, or unsigned long, depending on the highest-ranking operand involved.