C++浮点与整型提升详解

在对象大小与 sizeof 运算符 中,我们指出 C++ 对每种基本类型都规定了最小位宽,而其真实大小可随编译器及目标架构而异。

此灵活性旨在让 intdouble 等类型能够被设定为在特定架构上性能最优的位宽。例如,32 位计算机通常一次可处理 32 位数据;此时,int 多半被设为 32 位,因为这正是该 CPU 的“自然”数据宽度(也往往是最快的情形)。

提示
数据类型所用的比特数称为其宽度更宽的类型占更多位,更窄的类型占更少位。

当 32 位 CPU 需要修改一个 8 位值(如 char)或 16 位值时,会发生什么?部分 32 位处理器(如 32 位 x86)可直接操作 8 位或 16 位数据,但速度往往低于 32 位操作;另一些 32 位 CPU(如 32 位 PowerPC)只能操作 32 位数据,必须借助额外技巧来处理更窄的值。

数值提升(numeric promotion)

为在多种架构上兼顾可移植性与性能,C++ 语言设计者不愿假设 CPU 必然能高效处理小于其“自然”数据宽度的值。为此,C++ 定义了一类称为数值提升的类型转换:将某些较窄的数值类型(如 char)转换为较宽且 CPU 能有效处理的类型(通常为 intdouble)。

所有数值提升均为保值转换(value-preserving conversion,又称安全转换),即源类型的任何可能值都能在目标类型中得到相等值。由于提升是安全的,编译器会按需自动执行且不会给出警告。

数值提升减少冗余
数值提升还解决了另一难题。假设我们要写一个打印 int 的函数:

#include <iostream>

void printInt(int x)
{
    std::cout << x << '\n';
}

若不存在类型转换,为了也能打印 shortchar,就得再为 short 写一个函数,再为 char 写一个……还得顾及 unsigned charsigned charunsigned shortwchar_tchar8_tchar16_tchar32_t,很快就会失控。

数值提升拯救了我们:只需编写参数为 int 和/或 double 的函数(如上例的 printInt),即可用任何可被数值提升到这些类型的实参来调用。

数值提升的分类

数值提升规则分为两大类:整型提升(integral promotions)与浮点提升(floating point promotions)。仅列于这两类的转换才被视作数值提升。

浮点提升

较为简单:
根据浮点提升规则,类型 float 的值可转换为类型 double
因此,可以编写接受 double 的函数,并用 doublefloat 实参调用:

#include <iostream>

void printDouble(double d)
{
    std::cout << d << '\n';
}

int main()
{
    printDouble(5.0);   // 无需转换
    printDouble(4.0f);  // float → double 数值提升
    return 0;
}

第二次调用时,float 字面量 4.0f 被提升为 double,使实参类型与形参匹配。

整型提升
规则更为复杂。按整型提升规则,可进行以下转换:

  • signed charsigned shortint
  • unsigned charchar8_tunsigned shortint(若 int 能容纳该类型全部取值范围),否则 → unsigned int
  • char 默认为 signed,则遵循 signed char 规则;若默认为 unsigned,则遵循 unsigned char 规则
  • boolintfalse 变 0,true 变 1

在 8 位字节且 int 至少 4 字节的常见环境下,上述规则意味着 boolcharsigned charunsigned charsigned shortunsigned short 均被提升为 int

另有少量更少见的整型提升规则,可参阅 cppreference 相关章节

多数情况下,我们可编写参数为 int 的函数,并用多种整型实参调用:

#include <iostream>

void printInt(int x)
{
    std::cout << x << '\n';
}

int main()
{
    printInt(2);

    short s{ 3 };      // 无 short 字面量后缀,用变量演示
    printInt(s);       // short → int 数值提升

    printInt('a');     // char → int 数值提升
    printInt(true);    // bool → int 数值提升

    return 0;
}

有两点值得注意:

  1. 在某些架构(如 int 仅 2 字节)上,部分无符号整型可能被提升为 unsigned int 而非 int
  2. 某些较窄的无符号类型(如 unsigned char)可能被提升为较宽的有符号类型(如 int)。因此,整型提升虽保值,却不一定保留符号性(signed/unsigned)。

并非所有加宽转换都是数值提升

某些加宽转换(如 charshortintlong)在 C++ 中不被视为数值提升,而被归为数值转换(numeric conversions,将在 10.3 课讨论)。原因在于这些转换并不能帮助把较小类型转换为 CPU 更高效处理的大类型。

这一区别多数情况下仅属学术范畴。然而,在特定情形下,编译器会优先选择数值提升而非数值转换。我们将在 11.3 课 —— 函数重载决议与二义性匹配 中看到相关示例。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"