5-3 数制(十进制、二进制、十六进制和八进制)
作者注
本节内容为可选学习内容。
后续课程将涉及十六进制数,因此在继续学习前,您至少应对此概念有基本了解。
在日常生活中,我们使用十进制decimal计数,其中每个数字位可以是0、1、2、3、4、5、6、7、8或9。十进制也称为“基数为10base 10”,因为它有10个可能的数字(0到9)。在此系统中,计数方式如下:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … C++程序中的数字默认即采用十进制表示。
int x { 12 }; // 12 is assumed to be a decimal number
在二进制binary中,只有两个数字:0和1,因此称为“基数为2base 2”。在二进制中,计数方式如下:0, 1, 10, 11, 100, 101, 110, 111, … 为便于阅读,较长的二进制数常以空格分隔为4位一组(例如 1101 0100)。
十进制和二进制都是数制体系numeral systems的例子,数制体系是表示数字的符号集合(如数字)的正式名称。C++支持四种主要数制体系,按使用频率排序依次为:十进制(基数10)、二进制(基数2)、十六进制(基数16)和八进制(基数8)。
命名规则
在十进制和二进制中,数字0和1具有相同含义。两种系统都称这些数字为“零”和“一”。
那么数字10呢?10是数制中最后一位单数字之后出现的数字。在十进制中,10等于九加一,我们称之为“十”。
在二进制中,10使用相同数字,但表示1加1(相当于十进制中的2)。若将二进制10称为“十”会造成混淆,因为“十”代表九加一,而此处的10代表一加一。
因此,“十”“十一”“十二”等名称通常专用于十进制数。在非十进制数系中,我们更倾向于称这些数为“一零”“一一”“一二”等。二进制101并非“一百零一”,而是“一零一”。
八进制和十六进制数值
八进制Octal是以8为基数的计数系统——即仅使用以下数字:0、1、2、3、4、5、6、7。八进制计数方式如下:0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, …(注意:没有8和9,因此从7直接跳到10)。
| Decimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Octal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 |
要使用八进制字面量,请在字面量前添加一个 0(零):
#include <iostream>
int main()
{
int x{ 012 }; // 0 before the number means this is octal
std::cout << x << '\n';
return 0;
}
该程序输出:

为什么是10而不是12?因为数字默认以十进制输出,而12八进制等于10十进制。
八进制极少使用,建议避免采用。
十六进制Hexadecimal是以16为基数的计数系统。其计数方式如下:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, …
| Decimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Hexadecimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 10 | 11 |
您也可以使用小写字母(尽管大写字母更为常见)。
要使用十六进制字面量,请在字面量前添加前缀 0x:
#include <iostream>
int main()
{
int x{ 0xF }; // 0x before the number means this is hexadecimal
std::cout << x << '\n';
return 0;
}
该程序输出:

您也可以使用 0X 前缀,但 0x 是惯例用法,因为它更易于阅读。
数制对照表
以下是四种数制numeral systems并列展示,以便更清晰地观察它们各自的演进过程:
| Decimal | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Binary | 1 | 10 | 11 | 100 | 101 | 110 | 111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
| Octal | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| Hexadecimal | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
| Decimal | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Binary | 10000 | 10001 | 10010 | 10011 | 10100 | 10101 | 10110 | 10111 | 11000 | 11001 | 11010 | 11011 | 11100 | 11101 | 11110 | 11111 |
| Octal | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
| Hexadecimal | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1A | 1B | 1C | 1D | 1E | 1F |
每行都遵循相同的模式:最右侧的数字从0递增至(基数-1)。当该数字达到(基数)时,它将重置为0,而左侧的数字则递增1。若该左侧数字达到(基数),则重置为0,其左侧的数字递增1。如此循环往复……
使用十六进制表示二进制
由于十六进制数字有16种不同的值,可以说单个十六进制数字包含4位。因此,一对十六进制数字可精确表示一个完整的字节。
| Hexadecimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Binary | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
考虑一个二进制值为 0011 1010 0111 1111 1001 1000 0010 0110 的 32 位整数。由于位数较长且存在重复,这种表示方式不易阅读。在十六进制中,相同值表示为 3A7F 9826,显得更为简洁。正因如此,十六进制值常被用于表示内存地址或内存中的原始数据(其类型未知)。
二进制字面量
在C++14之前,不支持二进制字面量。不过十六进制字面量提供了一个实用的替代方案(在现有代码库中仍可能见到):
#include <iostream>
int main()
{
int bin{}; // assume 16-bit ints
bin = 0x0001; // assign binary 0000 0000 0000 0001 to the variable
bin = 0x0002; // assign binary 0000 0000 0000 0010 to the variable
bin = 0x0004; // assign binary 0000 0000 0000 0100 to the variable
bin = 0x0008; // assign binary 0000 0000 0000 1000 to the variable
bin = 0x0010; // assign binary 0000 0000 0001 0000 to the variable
bin = 0x0020; // assign binary 0000 0000 0010 0000 to the variable
bin = 0x0040; // assign binary 0000 0000 0100 0000 to the variable
bin = 0x0080; // assign binary 0000 0000 1000 0000 to the variable
bin = 0x00FF; // assign binary 0000 0000 1111 1111 to the variable
bin = 0x00B3; // assign binary 0000 0000 1011 0011 to the variable
bin = 0xF770; // assign binary 1111 0111 0111 0000 to the variable
return 0;
}
从 C++14 开始,我们可以使用 0b 前缀来表示二进制字面量:
#include <iostream>
int main()
{
int bin{}; // assume 16-bit ints
bin = 0b1; // assign binary 0000 0000 0000 0001 to the variable
bin = 0b11; // assign binary 0000 0000 0000 0011 to the variable
bin = 0b1010; // assign binary 0000 0000 0000 1010 to the variable
bin = 0b11110000; // assign binary 0000 0000 1111 0000 to the variable
return 0;
}
数字分隔符
由于长数字字面量难以阅读,C++14还新增了使用单引号(')作为数字分隔符的功能。
#include <iostream>
int main()
{
int bin { 0b1011'0010 }; // assign binary 1011 0010 to the variable
long value { 2'132'673'462 }; // much easier to read than 2132673462
return 0;
}
另请注意,分隔符不能出现在值的首个数字之前:
int bin { 0b'1011'0010 }; // error: ' used before first digit of value
数字分隔符纯粹是视觉元素,不会以任何方式影响字面值。
输出十进制、八进制或十六进制数值
默认情况下,C++以十进制形式输出数值。但可通过使用std::dec、std::oct和std::hex输入输出操作符更改输出格式:
#include <iostream>
int main()
{
int x { 12 };
std::cout << x << '\n'; // decimal (by default)
std::cout << std::hex << x << '\n'; // hexadecimal
std::cout << x << '\n'; // now hexadecimal
std::cout << std::oct << x << '\n'; // octal
std::cout << std::dec << x << '\n'; // return to decimal
std::cout << x << '\n'; // decimal
return 0;
}
这将输出:

请注意,一旦应用,I/O 操作符将保持设置状态,用于未来的输出,直到再次更改为止。
以二进制形式输出值
以二进制形式输出值稍显复杂,因为 std::cout 并未内置此功能。所幸 C++ 标准库中包含名为 std::bitset 的类型(位于
使用 std::bitset 时,需定义该变量并指定存储位数。位数必须是编译时常量。std::bitset 可通过整数值初始化(支持十进制、八进制、十六进制或二进制等任意格式)。
#include <bitset> // for std::bitset
#include <iostream>
int main()
{
// std::bitset<8> means we want to store 8 bits
std::bitset<8> bin1{ 0b1100'0101 }; // binary literal for binary 1100 0101
std::bitset<8> bin2{ 0xC5 }; // hexadecimal literal for binary 1100 0101
std::cout << bin1 << '\n' << bin2 << '\n';
std::cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it
return 0;
}
这将输出:

在上面的代码中,这一行:
std::cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it
创建一个临时(未命名的)std::bitset对象,包含4位,用二进制字面量0b1010初始化它,打印其二进制值,然后丢弃该临时对象。
相关内容
我们在第O.1课中更详细地介绍了std::bitset——通过std::bitset实现位标志与位操作。
使用格式化/打印库高级功能输出二进制值(进阶)
在C++20和C++23中,我们通过全新的格式化库(C++20)和打印库(C++23)获得了更优的二进制输出方案:
#include <format> // C++20
#include <iostream>
#include <print> // C++23
int main()
{
std::cout << std::format("{:b}\n", 0b1010); // C++20, {:b} formats the argument as binary digits
std::cout << std::format("{:#b}\n", 0b1010); // C++20, {:#b} formats the argument as 0b-prefixed binary digits
std::println("{:b} {:#b}", 0b1010, 0b1010); // C++23, format/print two arguments (same as above) and a newline
return 0;
}
这将输出:

测验时间
问题 #1
根据上表,32 在二进制和十六进制中分别表示什么?
显示解答
Binary: 10 0000
Hex: 20

浙公网安备 33010602011771号