Because C++ is designed to be portable and performant across a wide range of architectures, the language designers did not want to assume a given CPU would be able to efficiently manipulate values that were narrower than the natural data size for that CPU.
However, one consequence of this portability is that fundamental integer types do not always have the same size on every platform.
There are 3 different widths of an int
| Type | Common Size (Modern Systems) | Guaranteed Minimum Size |
|---|---|---|
| short | 2 bytes (16 bits) | 16 bits |
| int | 4 bytes (32 bits) | 16 bits |
| long | 4 or 8 bytes* | 32 bits |
| long long | 8 bytes (64 bits) | 64 bits |
The number of bits a data type uses is called its width. A wider data type is uses more bits, and a narrower data type uses less bits.
Consider the case where you wanted to write a function to print a value of type int:
#include <iostream>
void printInt(int x)
{
std::cout << x << '\\n';
}
What happens if we want to call this function but the argument is a char, unsigned short, wchar_t, long long?
We’d have to make separate functions to be compatible with each alias! It would look like this:
void printChar(char x);
void printShort(short x);
void printBool(bool x);
void printUnsignedShort(unsigned short x);
Luckily we don’t thanks to numeric promotions. The argument automatically gets promoted to an int.
Introducing numeric promotions
This is the purpose of numeric promotions. This is when a type transforms from a narrow to a wider width.
C++ has 2 types of numeric promotions:
integral promotionsfloating point promotions
Integral Promotions
Any type that has a smaller width than an int gets converted into an int
| Original Type | Promoted Type | Notes |
|---|---|---|
bool | int | false becomes 0, true becomes 1 |
char / signed char | int | Standard character promotion |
unsigned char | int | Promotes to int because int can hold 0–255 |
short | int | Standard promotion |
unsigned short | int or unsigned int | Becomes unsigned int only if int is also 16-bit |
enum | int | Promotes to its underlying integral type’s promotion |
If the argument has a smaller width then the function parameter, numeric promotions takes place.
#include <iostream>
void printInt(int x) { std::cout << x << " is an int " << '\\n';}
int main() {
short s { 3 }; // initialize short
printInt(s); // short -> int
printInt('d'); // char -> int
printInt(false); // bool -> int
}
// Output
3 is an int
100 is an int
0 is an int
Floating point promotions
This is when a float gets promoted to a double.
void acceptDouble(double x) {
std::cout << x << '\\n';
}
int main() {
acceptDouble(12.4f) // float -> double
}
NOTE: Numeric promotions are a subset of numeric conversions. A promotion is a widening conversion whilst a conversion can result in a truncation of information. E.g. double → int.
Ultimately
Numeric promotions are one of the quiet mechanisms that make C++ both portable and efficient. They consist of integral and floating point promotions.
Integral promotions allow us to create functions that accept an int of a smaller width, saving us with overloading the function. The compiler does this:
This short is too small for the CPU to work with efficiently; turn it into an int before doing the math
Similarly, floating point promotions widen the width of the argument, making it compatible with the function.