介绍:为什么数制很重要
欢迎来到计算机科学数据表示的世界!这一章起初看起来可能纯粹是数学,但它却是计算机工作方式的绝对基石。为什么这么说?因为计算机既不会说英语,也不使用十进制(Base 10);它们使用的是二进制(Base 2)——即由 0 和 1 组成的序列。
理解数制及其转换,能让你清楚地看到数据是如何在处理器内部存储、操作和表示的。这些知识对于后续学习汇编语言、浮点数以及数据存储计算等主题至关重要。
1. 核心数制 (3.5.1)
1.1 十进制 (Base 10) – 我们日常使用的系统
我们每天都在使用十进制。它是十进制,因为它使用了 10 个唯一的数字(0 到 9)。
- 基数 (Base): 10
- 使用的数字: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
我们通过位值表示法 (Positional Notation) 来理解数值,其中数字的位置决定了它的 10 的幂次方。
示例: 十进制数 459 实际上是:
$$4 \times 10^2 + 5 \times 10^1 + 9 \times 10^0$$
1.2 二进制 (Base 2) – 计算机的语言
计算机使用二进制,因为电子电路只能代表两种状态:开 (1) 或关 (0)。每一个 0 或 1 被称为一个位 (Bit)(二进制数字)。
- 基数 (Base): 2
- 使用的数字: 0, 1
二进制也使用位值表示法,但它是基于 2 的幂次方。
示例: 一个 8 位的二进制数使用以下列权重:
$$128, 64, 32, 16, 8, 4, 2, 1$$
1.3 十六进制 (Base 16) – 程序员的速记法
二进制字符串(如 110101100101)很快就会变得很长,人类读起来和记起来都很困难。十六进制 (Hex) 是二进制的一种便捷缩写。
- 基数 (Base): 16
- 使用的数字: 0-9 和 A-F
十六进制使用 16 个唯一的符号。由于我们只有 10 个数字,因此字母 A 到 F 被用来表示 10 到 15 的值。
| 十进制 | 二进制 (4位) | 十六进制 |
|---|---|---|
| 0 | 0000 | 0 |
| 9 | 1001 | 9 |
| 10 | 1010 | A |
| 15 | 1111 | F |
2. 数制转换 (3.5.1)
你必须能够在这三种进制之间进行转换:十进制 (Base 10)、二进制 (Base 2) 和十六进制 (Base 16)。
2.1 二进制转十进制 (Base 2 转 Base 10)
这是最简单的转换:将每一位乘以其对应的 2 的幂次方,然后将结果相加。
步骤示例:将 \(101101_2\) 转换为十进制。
- 写出位置列标题(2 的幂次方,从右侧的 \(2^0 = 1\) 开始)。
- 将二进制数字填入对应的列中。
- 将所有出现 '1' 的对应列数值相加。
| $2^5 (32)$ | $2^4 (16)$ | $2^3 (8)$ | $2^2 (4)$ | $2^1 (2)$ | $2^0 (1)$ |
|---|---|---|---|---|---|
| 1 | 0 | 1 | 1 | 0 | 1 |
$$32 + 0 + 8 + 4 + 0 + 1 = 45$$
结果:\(101101_2 = 45_{10}\)
2.2 十进制转二进制 (Base 10 转 Base 2)
对于较大的数字,最可靠的方法是重复除以 2 法,并将余数逆序排列。
步骤示例:将 \(45_{10}\) 转换为二进制。
- 将十进制数除以 2。
- 记录余数(0 或 1)。
- 用商重复上述过程,直到商为 0。
- 从下(最高有效位 MSB)到上(最低有效位 LSB)读取余数。
| 除法运算 | 商 | 余数 (位) |
|---|---|---|
| $45 \div 2$ | 22 | 1 |
| $22 \div 2$ | 11 | 0 |
| $11 \div 2$ | 5 | 1 |
| $5 \div 2$ | 2 | 1 |
| $2 \div 2$ | 1 | 0 |
| $1 \div 2$ | 0 | 1 |
向上读取:101101。
结果:\(45_{10} = 101101_2\)
转换要点: 二进制与十六进制的转换对于理解数据的内部结构(如内存地址或指令代码)至关重要。
2.3 二进制与十六进制的转换
这种转换既直接又简单,因为 \(16 = 2^4\)。一个十六进制位恰好完美对应四个二进制位。
二进制转十六进制:四位一组
从右侧(LSB)开始,将二进制数字每四个分成一组。将每一组转换为对应的十六进制数。
示例:将 \(1101011011_2\) 转换为十六进制。
- 在左侧补零以凑满四位一组:0011 0101 1011
- 转换每一组:
- $0011_2 = 3_{16}$
- $0101_2 = 5_{16}$
- $1011_2 = B_{16}$ (因为 $11_{10} = B_{16}$)
- 组合结果:35B
结果:\(1101011011_2 = 35B_{16}\)
十六进制转二进制:每一位展开
将每一个十六进制位转换为其对应的 4 位二进制序列。
示例:将 \(E7_{16}\) 转换为二进制。
- 转换 E:$E_{16} = 14_{10} = 1110_2$
- 转换 7:$7_{16} = 7_{10} = 0111_2$
- 组合结果:11100111
结果:\(E7_{16} = 11100111_2\)
快速回顾:转换小贴士
二进制 $\leftrightarrow$ 十六进制: 请务必使用 4 位分组快捷法。它快速且不易出错。
十进制转二进制: 使用重复除以 2 法,并向上读取余数。
二进制转十进制: 写出 2 的幂次方列标题(128, 64, 32, 16, 8, 4, 2, 1),并将位置为 '1' 的数值相加。
3. 信息单位 (3.5.2)
3.1 位与字节
最小的信息单位是位 (Bit, b)。一位存储一个 0 或 1。
计算机内存和存储中使用的基本单位是字节 (Byte, B)。
- 一个字节是由 8 位组成的。
- 一个字节可以表示 \(2^8 = 256\) 种不同的值(从 0 到 255)。
3.2 \(2^n\) 规则
如果你有 \(n\) 位,总共可以表示 \(2^n\) 种不同的值。
你知道吗? 计算机系统中可寻址的内存总量也取决于地址总线的位数(例如,32 位地址总线可以访问 \(2^{32}\) 个位置)。
示例:
- 2 位可以表示 \(2^2 = 4\) 种值(00, 01, 10, 11)。
- 3 位可以表示 \(2^3 = 8\) 种值(000 到 111)。
- 16 位可以表示 \(2^{16} = 65,536\) 种值。
3.3 十进制前缀 vs. 二进制前缀
在讨论存储容量(KB, MB 等)时,存在两种基于 10 的幂次方或 2 的幂次方的系统。区分标准的十进制前缀(kilo, mega)和二进制前缀(kibi, mebi)至关重要。
十进制前缀(10 的幂次方)
这些是数学和大多数存储制造商(如硬盘)使用的标准单位。
| 名称 | 符号 | 10 的幂次方 | 近似值 |
|---|---|---|---|
| kilo | k | $10^3$ | 1,000 |
| mega | M | $10^6$ | 1,000,000 |
| giga | G | $10^9$ | 1,000,000,000 |
| tera | T | $10^{12}$ | $10^{12}$ |
示例:1 kB (kilobyte) = $10^3$ 字节 (1,000 字节)
二进制前缀(2 的幂次方)
这些前缀被计算机系统用来指代内存和数据大小,因为计算机的一切都是基于 2 的幂次方的。标准的二进制前缀在中间加入了 "bi"(kibi, mebi, gibi, tebi)。
| 名称 | 符号 | 2 的幂次方 | 精确值 |
|---|---|---|---|
| kibi | Ki | $2^{10}$ | 1,024 |
| mebi | Mi | $2^{20}$ | 1,048,576 |
| gibi | Gi | $2^{30}$ | $2^{30}$ |
| tebi | Ti | $2^{40}$ | $2^{40}$ |
示例:1 KiB (kibibyte) = $2^{10}$ 字节 (1,024 字节)
类比: 把这想象成测量长度。十进制的 kilo 刚好是 1000 米,而二进制的 kibi 稍微长一点,是 1024 米。
4. 用二进制表示整数 (3.5.3)
4.1 无符号二进制 (3.5.3.1)
无符号二进制 (Unsigned binary) 用于表示正整数(和零)。术语“无符号”意味着没有专门的位来指示数字是正还是负。
- 所有的位都对数字的大小做出贡献。
- 最小值始终为 0。
- 对于 \(n\) 位,最大值为 \(2^n - 1\)。
示例: 在 8 位无符号二进制中,范围是 0 到 \(2^8 - 1\)(即 0 到 255)。
4.2 无符号二进制算术 (3.5.3.2)
二进制加法
二进制加法遵循简单的规则,就像十进制加法一样,但在和达到 2 时会发生进位。
| $A$ | $B$ | 和 | 进位 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 (进位 1) |
| 1 + 1 + 进位 1 | 1 | 1 (进位 1) |
示例:在 4 位中进行 \(5_{10} + 3_{10}\)
1 1 0 0 <-- 进位
0 1 0 1 (5)
+ 0 0 1 1 (3)
-----------------
1 0 0 0 (8)
处理溢出
如果加法的结果超出了分配的位数的最大范围,就会发生溢出 (Overflow)。这个额外的位会丢失,导致结果错误。
示例: 在 4 位无符号二进制中添加 \(15 + 1\):
1 1 1 1 (15)
+ 0 0 0 1 (1)
-----------------
(1) 0 0 0 0 (0 - 错误!)
第 5 位(1)是溢出位,它被丢弃了,这意味着 \(15 + 1\) 看起来等于 0。
二进制乘法
二进制乘法使用简单的移位和加法,类似于十进制的长乘法,但我们只需要乘以 0 或 1。
示例:\(101_2 \times 11_2\) (\(5 \times 3\))
1 0 1
x 0 1 1
-------
1 0 1 (101 x 1)
+ 1 0 1 0 (101 x 1 左移一位)
-------
1 1 1 1 (15)
结果:\(1111_2 = 15_{10}\)
请务必记住二进制加法中的进位!如果你是在固定的位数内工作(如 8 位),任何超出最后一位的进位都是溢出。
4.3 使用补码 (Two's Complement) 表示有符号二进制 (3.5.3.3)
为了表示负数,我们使用有符号二进制 (Signed binary)。在本大纲中,你需要掌握的唯一方法是补码 (Two's Complement)。
在补码中,最高有效位 (MSB)(最左边的位)作为符号指示符:
- 如果 MSB 是 0,数字为正。
- 如果 MSB 是 1,数字为负。
将正十进制转换为补码
这与转换为无符号二进制相同,但如果你是在特定的位数长度内(例如 8 位)工作,则必须确保有一个前导零。
示例: +13 在 8 位中表示为 \(00001101_2\)。
将负十进制转换为补码
遵循“取反加一” (Flip and Add 1) 方法:
- 找到该数字的正二进制表示(例如,对于 -13,使用 +13)。
- 取反 (Flip) 所有位(0 变成 1,1 变成 0)。这就是反码 (One's Complement)。
- 将结果加 1。
步骤示例:将 \(-13_{10}\) 转换为 8 位补码。
- 正 13:\(00001101\)
- 位取反(反码):\(11110010\)
- 加 1:
11110010 + 1 -------- 11110011
结果:\(-13_{10} = 11110011_2\)。 (注意 MSB 是 1,确认它是负数)。
将补码转换回十进制
如果 MSB 是 0,像往常一样转换(它是正数)。
如果 MSB 是 1(它是负数),你可以使用这个快捷方法:将 MSB 视为负权重,并正常求和其余部分。
示例:将 \(11110011_2\) 转换回十进制 (8 位)。
| $-2^7 (-128)$ | $2^6 (64)$ | $2^5 (32)$ | $2^4 (16)$ | $2^3 (8)$ | $2^2 (4)$ | $2^1 (2)$ | $2^0 (1)$ |
|---|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 |
$$(-128) + 64 + 32 + 16 + 0 + 0 + 2 + 1 = -13$$
补码减法
在计算机算术中,减法 (A - B) 是通过加上 B 的负数来完成的:$$A - B = A + (-B)$$
步骤示例:在 8 位中进行 \(25_{10} - 10_{10}\)。
我们要执行的是 \(25 + (-10)\)。
- 正 25:\(00011001\)
- 负 10 (\(-10_{10}\)):
- 正 10:00001010
- 取反:11110101
- 加 1:11110110
- 将两个二进制数相加:
1 1 1 1 1 0 0 0 <-- 进位 0 0 0 1 1 0 0 1 (25) + 1 1 1 1 0 1 1 0 (-10) ------------------ (1) 0 0 0 0 1 1 1 1 (15)
最后的进位(括号中的)在补码算术中被丢弃。剩下的 8 位 \(00001111_2\),转换为 \(15_{10}\)。
结果:减法通过加法成功完成。
补码的 \(n\) 位范围
由于其中一位专门用于符号位,所以最大范围比无符号二进制要小。
对于 \(n\) 位,范围是:
$$\text{最小值:} -2^{n-1}$$ $$\text{最大值:} +2^{n-1} - 1$$
示例: 对于 8 位 (\(n=8\)):
- 最小值:$-2^{(8-1)} = -2^7 = -128$
- 最大值:$2^{(8-1)} - 1 = 2^7 - 1 = 127$
关键总结:二进制表示
选择无符号还是有符号(补码)决定了你可以存储的数字范围。无符号为你提供了最大的正数范围(例如 8 位中的 0 到 255),而补码允许你存储正整数和负整数(例如 8 位中的 -128 到 127)。