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;
}

image

在第二次调用 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;
}

image

这里有两点值得注意。首先,在某些架构(例如采用2字节整数的架构)中,某些无符号整数类型可能被提升为无符号整型(unsigned int)而非有符号整型(int)。

其次,某些较窄的无符号类型(如无符号字符类型)可能被提升为更大的有符号类型(如整数类型)。因此,尽管整数提升会保留值本身,但未必能保持类型的符号性(有符号/无符号)。


并非所有扩展转换都是数值提升

某些扩展型转换(如char转short或int转long)在C++中不被视为数值提升(它们属于数值转换,我们将在第10.3节“数值转换”中详细讨论)。这是因为此类转换不符合“将较小类型转换为更大类型以提升处理效率”的目标。

这种区分主要具有理论意义。但在特定情况下,编译器会优先选择数值提升而非数值转换。当我们讲解函数重载解析时(详见后续第11.3节——函数重载解析与模糊匹配),将通过实例展示这种差异带来的影响。

posted @ 2026-03-02 16:36  游翔  阅读(1)  评论(0)    收藏  举报