10-2 浮点数与整数提升
在第4.3课——对象大小与sizeof运算符中,我们提到C++为每种基本类型提供了最小尺寸保证。但这些类型的实际大小会因编译器和架构而异。
这种可变性允许int和double数据类型被设置为在特定架构上实现性能最大化的尺寸。例如,32位计算机通常能同时处理32位数据。此时int类型很可能被设定为32位宽度,因为这是CPU操作数据的“自然”大小(且通常能实现最佳性能)。
重要提示
数据类型的位数称为其宽度。宽数据类型使用更多位,窄数据类型使用更少位。
那么当需要32位CPU修改8位值(如char类型)或16位值时会发生什么?部分32位处理器(如32位x86 CPU)可直接操作8位或16位值,但效率通常低于处理32位值。而另一些32位CPU(如32位PowerPC CPU)仅支持32位操作,需借助特殊机制处理窄数据类型。
数值提升
由于C++旨在实现跨多种架构的可移植性和高性能,语言设计者不愿假设特定CPU能高效处理小于其自然数据尺寸的值。
为应对这一挑战,C++定义了一类非正式称为“数值提升”的类型转换机制。数值提升numeric promotion是指将特定窄数值类型(如char)转换为特定宽数值类型(通常为int或double)的过程,后者能实现高效处理。
所有数值提升都保持值的完整性。值保持转换value-preserving conversion(也称安全转换safe conversion)指所有源值都能转换为目标类型的等值。
由于提升机制安全可靠,编译器会根据需要自由使用数值提升,且不会发出警告。
数值提升减少冗余
数值提升还解决了另一个问题。假设需要编写一个打印 int 类型值的函数:
#include <iostream>
void printInt(int x)
{
std::cout << x << '\n';
}
虽然这很简单,但如果还需打印 short 类型或 char 类型的值呢?若没有类型转换机制,我们必须分别编写short型和char型的打印函数。更别提还需要为unsigned char、signed char、unsigned short、wchar_t、char8_t、char16_t和char32_t分别编写版本!由此可见这种做法很快就会变得难以管理。
数值提升在此派上用场:我们可以编写带有 int 和/或 double 形参的函数(如上文的 printInt() 函数)。随后,该代码可接受能通过数值提升转换为函数形参类型的实参进行调用。
数值提升分类
数值提升规则分为两大子类:整数提升与浮点提升。仅这些类别中列出的转换才被视为数值提升。
浮点数提升
我们先从较简单的规则开始。
根据浮点数提升floating point promotion规则,float类型的值可转换为double类型的值。
这意味着我们可以编写一个接受 double 类型的函数,并用 double 或 float 值调用它:
#include <iostream>
void printDouble(double d)
{
std::cout << d << '\n';
}
int main()
{
printDouble(5.0); // no conversion necessary
printDouble(4.0f); // numeric promotion of float to double
return 0;
}

在第二次调用 printDouble() 时,浮点字面量 4.0f 被提升为 double,使实参类型与函数形参类型匹配。
整数提升
整数提升规则更为复杂。
根据整数提升integral promotion规则,可进行以下转换:
- 有符号 char 或有符号 short 可转换为 int。
- 无符号 char、char8_t 和无符号 short 可转换为 int(若 int 能容纳该类型的完整范围),否则转换为无符号 int。
- 若 char 默认为有符号类型,则遵循上述有符号 char 的转换规则;若默认为无符号类型,则遵循上述无符号 char 的转换规则。
- bool 可转换为 int,其中 false 变为 0,true 变为 1。
假设字节为 8 位,整型大小为 4 字节或更大(这在当今很常见),上述规则基本意味着 bool、char、有符号 char、无符号 char、有符号 short 和无符号 short 都会被提升为 int。
另有若干较少使用的整数提升规则,详见 https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion。
这通常使我们能编写接受 int 形参的函数,并支持多种其他整数的类型。例如:
#include <iostream>
void printInt(int x)
{
std::cout << x << '\n';
}
int main()
{
printInt(2);
short s{ 3 }; // there is no short literal suffix, so we'll use a variable for this one
printInt(s); // numeric promotion of short to int
printInt('a'); // numeric promotion of char to int
printInt(true); // numeric promotion of bool to int
return 0;
}

这里有两点值得注意。首先,在某些架构(例如采用2字节整数的架构)中,某些无符号整数类型可能被提升为无符号整型(unsigned int)而非有符号整型(int)。
其次,某些较窄的无符号类型(如无符号字符类型)可能被提升为更大的有符号类型(如整数类型)。因此,尽管整数提升会保留值本身,但未必能保持类型的符号性(有符号/无符号)。
并非所有扩展转换都是数值提升
某些扩展型转换(如char转short或int转long)在C++中不被视为数值提升(它们属于数值转换,我们将在第10.3节“数值转换”中详细讨论)。这是因为此类转换不符合“将较小类型转换为更大类型以提升处理效率”的目标。
这种区分主要具有理论意义。但在特定情况下,编译器会优先选择数值提升而非数值转换。当我们讲解函数重载解析时(详见后续第11.3节——函数重载解析与模糊匹配),将通过实例展示这种差异带来的影响。

浙公网安备 33010602011771号