摘要
程序中的整数不是数学中的整数:它是一个有限的位序列,用 个位恰好能容纳 个不同的值。这一课研究这种表示及其后果。它教你用二进制和十六进制写出一个整数,并用格式说明 {:b} 和 {:x} 加以核对;介绍 C++ 的整数类型、用 sizeof 测量的类型大小,以及用 std::numeric_limits 查询的取值范围;解释负数如何用二进制补码表示,以及取值范围为什么不对称;区分有符号整数和无符号整数,并研究运算超出取值范围时会发生什么:对无符号数,标准规定了结果,即模 运算;对有符号数,标准没有规定,程序失去一切保证。最后给出为每个数据选择类型的准则。
学习目标
- 在十进制、二进制和十六进制之间转换整数,并用格式说明
{:b}和{:x}核对转换结果。 - 计算 位整数在有符号和无符号时的取值范围,并用
std::numeric_limits和sizeof加以核对。 - 解释二进制补码,并写出一个较小负数的八位表示。
- 区分有符号溢出(未定义行为)和无符号溢出(模 运算),并预测后者的结果。
- 根据数据可预见的取值范围选择合适的整数类型,在宽度重要时使用
<cstdint>中的定宽类型。
进位制
变量与内存一课把变量介绍为一个有名字、类型和值的存储单元。然而,内存保存的不是十进制数字,而是位,每一位的值是 0 或 1,每八位组成一个字节(标准要求至少八位;现今所有系统都恰好使用八位)。因此,要知道一个整数变量能取哪些值,就必须知道如何只用两个数字写出一个数,而这正是二进制这种进位制所做的事。
在基数为 的进位制中,每一位数字的值取决于它所在的位置:最后一位乘以 ,它前面的一位乘以 ,再前面的一位乘以 ,依此类推。在十进制中, 表示 。在二进制中,数字只有 0 和 1,各位的权是 2 的幂:。由此得出从二进制化为十进制的规则:把有 1 的那些位置的权相加。
图中读取八位的位模式 00101101。每个格子上方标着它的权;各个 1 从左到右逐个点亮,值随之累加它们的权。
证明
八个格子全部熄灭:值为 0。各位的权从右到左依次为 。
第一个 1 在权为 32 的位置:累计值为 32。
接着是权为 8 的位置:。
再接着是权为 4 的位置:。
最后一个在权为 1 的位置:。位模式
00101101表示数 45。把位四个一组分组,就得到十六进制数字:
0010是 2,1101是 13,在十六进制中写作d。因此,45 在十六进制中写作2d。
反方向的路,从十进制到二进制,用连续除法来走:把这个数除以 2,记下余数(0 或 1),再对商重复这一步,直到商为 0;把余数从最后一个读到第一个,就是二进制数字。对于 45:,,,,,;把余数倒过来读,得到 101101,左边补上两个 0,就是图中的位模式。道理在于:每个余数都是被除的那个数的个位数字,而每个商都是那个数向右移动一位的结果。同样的方法适用于任何基数:对十六进制就除以 16,余数 10 到 15 用字母 a 到 f 表示。
基数 16,即十六进制,是为了方便而存在的:由于 ,每个十六进制数字恰好对应四个位,一个字节用两个数字就能写出。一个 32 位的数用二进制要写三十二个数字,很难读;用十六进制只要八个,而且不必计算就能逐个数字地转换成二进制。所以程序员用十六进制阅读内存。进位制的数学基础在“代数与几何 I”课程中的“进位记数制”一课中研究。
C++ 允许直接用这些进位制书写整数:以 0b 开头的字面量是二进制的,以 0x 开头的是十六进制的。在任何字面量中都可以加一个撇号作为数字分隔符,编译器会忽略它,读者则会感激它:0b0010'1101,1'000'000。输出时,格式说明 {:b} 以二进制写出整数,{:x} 以小写字母的十六进制写出,{:X} 则用大写字母;符号 # 加上前缀,而前面带一个 0 的宽度会在左边补 0。
#include <print>int main(){ const int decimal{45}; const int binary{0b0010'1101}; const int hexadecimal{0x2d}; std::println("{} {} {}", decimal, binary, hexadecimal); std::println("二进制:{:b}", decimal); std::println("八位:{:08b}", decimal); std::println("十六进制:{:x} {:X} {:#x}", decimal, decimal, decimal);}45 45 45二进制:101101八位:00101101十六进制:2d 2D 0x2d三个变量保存的是同一个值,只是用三种进位制写出,输出的第一行显示了这一点:进位制是写法的性质,而不是数的性质。在这三种情况下,内存保存的都是同一个位模式。格式说明 {:b} 省略左边的 0;{:08b} 补足八位。
正在加载内容…
已完成
登录账户以保存您的进度