整数类型
要使用标准化的整数类型,请始终包含 <cstdint> 和 <cstddef> 头文件:
#include <cstdint>
#include <cstddef> //::std::size_t and ::std::ptrdiff_t are in cstddef
对于 N 位整数: 无符号范围 = 0 到 2^N − 1 有符号范围 = −2^(N−1) 到 2^(N−1) − 1
计算字节数的公式:bytes = N / 8。
| 位 | 字节 | 无符号范围 | 有符号范围 |
|---|---|---|---|
| 8 | 1 | 0 到 255 | -128 到 127 |
| 16 | 2 | 0 到 65535 | -32768 到 32767 |
| 32 | 4 | 0 到 4294967295 | -2147483648 到 2147483647 |
| 64 | 8 | 0 到 18446744073709551615 | -9223372036854775808 到 9223372036854775807 |
| 128 | 16 | 0 到 340282366920938463463374607431768211455 | -170141183460469231731687303715884105728 到 170141183460469231731687303715884105727 |
最小、最快与精确宽度类型
C++ 定义了三族整数类型:
::std::uintXX_t/::std::intXX_t—— 精确宽度类型(仅当平台确实有 XX 位整数时才存在)。::std::uint_leastXX_t/::std::int_leastXX_t—— 至少有 XX 位的最小类型(总是存在)。::std::uint_fastXX_t/::std::int_fastXX_t—— 至少有 XX 位的最快类型。
为什么精确宽度类型可能不存在
精确宽度类型是可选的。在以下情况下它们可能缺失:
- 平台把字节定义为 16 位而不是 8 位,因此 8 位类型不可能存在。
- 工具链用浮点类型模拟整数(例如把 C++ 翻译成另一种语言时)。
- 厂商即便硬件能够支持,也干脆没有定义这些 typedef。
在绝大多数平台上这不是问题,但如果你想要最强的可移植性,就应当使用 ::std::int_leastXX_t 与 ::std::uint_leastXX_t。
如果 ::std::uintXX_t 确实存在,那么它保证与 ::std::uint_leastXX_t 相同。
示例
#include <cstdint>
#include <fast_io.h>
int main() {
using namespace ::fast_io::iomnp;
::std::int_least32_t a = -123; // guaranteed at least 32 bits
::std::uint_least64_t b = 1000; // guaranteed at least 64 bits
::std::int_fast16_t c = 42; // fastest type with ≥16 bits
::std::uint8_t d = 255; // exact 8-bit type (if available)
println("a = ", a, "\n" // prints -123
"b = ", b, "\n" // prints 1000
"c = ", c, "\n" // prints 42
"d = ", d); // prints 255
}
最佳实践
- 为最大可移植性使用
least类型(int_leastXX_t)——它们总是存在。 - 当性能比内存占用更重要时,使用
fast类型(int_fastXX_t)。 - 只有当你确实需要固定宽度时(例如二进制文件格式、硬件寄存器),才使用
exact类型(intXX_t)。 - 大小和计数优先使用
::std::size_t,因为它与平台的自然字长一致。
浮点类型
要使用标准化的浮点类型,请始终包含 <stdfloat> 头文件:
#include <stdfloat>
浮点数用来表示带小数的实数值。 它们能覆盖极大和极小的量级,但它们是近似的,会引入舍入误差。 与整数不同,浮点数值并不精确。
这些类型依照 IEC 559 标准(也就是 IEEE 754)定义, 该标准规定了浮点数如何表示,以及施加于它们的运算如何表现。
| 类型 | 位 | 字节 | 近似范围 | 精度 | 说明 |
|---|---|---|---|---|---|
::std::bfloat16_t |
16 | 2 | 约 10^±38 | 约 3 位十进制数字 | 主要用于 AI/ML 负载,以降低精度换取更快的运算 |
::std::float16_t |
16 | 2 | 约 10^±5 | 约 3–4 位十进制数字 | 半精度,常见于图形与嵌入式系统 |
::std::float32_t |
32 | 4 | 约 10^±38 | 约 7 位十进制数字 | 单精度,等价于 float |
::std::float64_t |
64 | 8 | 约 10^±308 | 约 15–16 位十进制数字 | 双精度,为了清晰起见优先于 double |
::std::float128_t |
128 | 16 | 约 10^±4932 | 约 33–34 位十进制数字 | 四精度,用于高精度科学计算 |
示例
// Define a reduced-precision floating point (AI/ML)
::std::bfloat16_t bf = 1.5f;
// Define a 16-bit floating point
::std::float16_t f16 = 3.14f;
// Define a 32-bit floating point
::std::float32_t f32 = 2.71828f;
// Define a 64-bit floating point
::std::float64_t f64 = 3.141592653589793;
// Define a 128-bit floating point
::std::float128_t f128 = 1.234567890123456789012345678901234Q;
为什么我们优先选择整数
整数是精确的:它们表示不带舍入的整数。 浮点类型对于科学和数学工作十分强大,但它们可能丢失精度。 用于计数、索引和大小表示时,整数更安全、更可预测。
另一个原因是可移植性:并非所有平台都支持浮点类型。 某些嵌入式系统或特殊环境可能缺少硬件浮点单元,或者模拟得极其缓慢。 即使支持浮点,你也可能受到限制——例如,Linux 内核不允许在内核空间进行浮点运算。 这意味着依赖浮点类型可能降低可移植性,或是在底层编程环境中引发问题。
因此,虽然浮点类型对于科学计算、图形学和机器学习不可或缺, 但整数仍然是通用编程任务中最可移植、最可靠的选择。
传统整数类型
在标准化固定宽度类型(::std::int_leastXX_t、::std::uint_leastXX_t)出现之前,
C++ 提供了传统整数类型:char、short、int、long、long long,以及它们的 unsigned 版本。
标准保证了什么
char保证恰好是1 字节。字节是 C++ 中最小可寻址存储单元(至少 8 位)。 标准没有规定char是有符号还是无符号——这由实现定义。short的最小大小为 16 位,且不能大于int。int的最小大小为 16 位,且不能大于long。long的最小大小为 32 位,且不能大于long long。long long的最小大小为 64 位。
这就是标准的全部保证。它没有固定确切大小。
例如,int 可以是 16 位、32 位、64 位,甚至荒谬的 100000 位——按标准来说依然合法。
这意味着我们不能依赖 int 在各平台上有可预测的大小。
示例
// Traditional integer types
// char is exactly 1 byte. Here we assign it an integer in [0,127],
// which corresponds to ASCII codes (e.g. 97 = 'a', but we will
// explain character literals later).
char c = 97; // implementation-defined signedness
short s = 123; // minimum 16 bits, no larger than int
int i = 456; // minimum 16 bits, no larger than long
long l = 789; // minimum 32 bits, no larger than long long
long long ll = 123456789LL; // minimum 64 bits
// Unsigned versions
unsigned short us = 123;
unsigned int ui = 1000;
unsigned long ul = 50000;
unsigned long long ull = 100000000ULL;
为什么我们优先选择标准化类型
由于 int、long 等类型的大小除了这些最小值与相对约束之外并无保证,
可移植代码应当使用 ::std::int_least32_t 或 ::std::uint_least64_t 这类标准化类型。
它们在跨平台时提供清晰、可预测的保证。
传统浮点类型
除整数外,C++ 还提供了传统浮点类型:
float、double 和 long double。
标准保证了什么
float是至少有 6 位十进制精度数字的浮点类型。double的精度至少不低于float,通常约提供 15 位十进制数字。long double的精度至少不低于double,依实现不同可能更高。
标准没有固定这些类型的确切大小或范围。
它只保证最低精度与次序:
在精度和范围上 float ≤ double ≤ long double。
在大多数现代平台上:
float是 32 位(单精度,IEEE 754)。double是 64 位(双精度,IEEE 754)。long double可能是 64 位、80 位(扩展精度),甚至 128 位,取决于编译器与架构。
由于标准只规定最低保证,你不能假定确切的大小或格式。
例如,在某些系统上 long double 可能与 double 完全相同,而在另一些系统上它可能提供扩展精度。
示例
// Traditional floating point types
float f = 3.14f; // at least 6 decimal digits precision
double d = 2.718281828; // at least as precise as float, usually ~15 digits
long double ld = 1.618033988749894L; // at least as precise as double, may be more
为什么优先使用标准化类型
正如整数一样,传统浮点类型只有最低保证。
为了跨平台的确定行为,现代 C++ 提供了标准化类型,例如
::std::float32_t、::std::float64_t 和 ::std::float128_t(由 IEC 559 / IEEE 754 标准定义)。
与传统类型不同,它们给出清晰、固定的大小与精度保证。
布尔类型
C++ 提供了专门的逻辑类型:bool。
与整数或浮点类型不同,bool 专门用于表示真值。
标准保证了什么
bool只能持有两个值:true和false。- 转换为整数时,
false变为0,true变为1。 - 把任何非零整数赋给
bool,结果为true。 bool的确切字节大小由实现定义,但保证足以存储两个值。
示例
// Boolean type
bool flag1 = true; // explicitly true
bool flag2 = false; // explicitly false
// Conversion from integers
bool b1 = 0; // becomes false
bool b2 = 1; // becomes true
bool b3 = 42; // any non-zero becomes true
bool b4 = -42; // any non-zero (including negative) becomes true
使用 fast_io 打印
#include <fast_io.h>
#include <cstddef>
#include <stdfloat>
int main()
{
using namespace ::fast_io::iomnp;
bool flag = true;
// Default printing: outputs 0 or 1
// With boolalpha: outputs textual true/false
println(flag, // prints "1"
" ",
boolalpha(flag)); // prints "true"
// boolalpha also applies to integers and floating point types.
// Any non-zero value will print as "true", zero prints as "false".
::std::size_t i = 42;
::std::float64_t d = 0.0;
println(boolalpha(i), // prints "true"
"\n",
boolalpha(d)); // prints "false"
}
用法
bool 主要用于条件、比较与控制流。
借助 fast_io,你可以用 boolalpha 选择把它打印成 0/1 还是 true/false。
同一个操纵器也可以用于整数和浮点值,把它们解释为逻辑真值。
字符数据类型
C++ 提供了多种字符类型,用于表示不同编码的文本。
虽然 char 最常见,现代 C++ 还引入了 char8_t、char16_t 和 char32_t 以支持 Unicode,另有 wchar_t 表示宽字符。
| 类型 | 大小 | 编码 | 示例 |
|---|---|---|---|
char |
1 字节 | 由实现定义(通常是 ASCII 或 UTF-8) | char c = 'A'; |
char8_t |
1 字节 | UTF-8 码元 | char8_t u8c = u8'A'; |
char16_t |
2 字节 | UTF-16 码元 | char16_t u16c = u'A'; |
char32_t |
4 字节 | UTF-32 码元 | char32_t u32c = U'A'; |
wchar_t |
由实现定义(2 或 4 字节) | 宽字符(依平台而定) | wchar_t wc = L'A'; |
#include <fast_io.h>
int main()
{
using namespace ::fast_io::iomnp;
char c = 'A';
char8_t u8c = u8'A';
char16_t u16c = u'A';
char32_t u32c = U'A';
wchar_t wc = L'A';
// Print numeric values
println(c); // prints 65
println(u8c); // prints 65
println(u16c); // prints 65
println(u32c); // prints 65
println(wc); // prints 65
// Print as characters
println(chvw(c)); // prints A
println(chvw(u8c)); // prints A
println(chvw(u16c)); // prints A
println(chvw(u32c)); // prints A
println(chvw(wc)); // prints A
}
重要:所有这些 char 类型在底层仍然都是整数类型。
字面量 'A' 存储为数值 65(它的 ASCII/Unicode 码点)。
这就是为什么调用 println(c) 会打印 65——你看到的是整数值。
要显示字符本身,你需要使用 chvw() 这类操纵器,它们会把整数解释为字符进行输出。
注意:打印 UTF 字符串字面量(u8"..."、u"..."、U"...")时,除非你指定了与字符类型匹配的设备,否则必须使用 code_cvt(...)。
默认情况下,print/println/scan 与 char 和 FILE* 绑定。
我们会在将来的 I/O 章节中详细讲解设备与编码。