第一讲 · 数制与数据表示
对应材料:2025 计组复习提纲“数制:数据的表示”,并参考课程概述中关于抽象层次的说明。
计算机里并没有一个天然的“整数”或“小数”。硬件真正保存的只有一串高低电平;我们先约定一种编码规则,再按这条规则解释比特。于是,同一个 11111111 可以是无符号数 、补码整数 ,也可以只是一个字节的数据。
这一讲要解决的核心问题不是“背编码”,而是:给定有限位数,怎样表示数、怎样运算、什么时候会失真。
1. 位权:所有进位制都在做同一件事
在基数为 的进位制中,
例如:
1.1 十进制整数转二进制
不断“除以 2 取余”,余数从后往前写。以 为例:
45 ÷ 2 = 22 ... 1
22 ÷ 2 = 11 ... 0
11 ÷ 2 = 5 ... 1
5 ÷ 2 = 2 ... 1
2 ÷ 2 = 1 ... 0
1 ÷ 2 = 0 ... 1
因此 。
1.2 十进制小数转二进制
不断“乘 2 取整数部分”,整数部分按出现顺序写。以 为例:
0.375 × 2 = 0.75 → 0
0.75 × 2 = 1.5 → 1
0.5 × 2 = 1.0 → 1
所以 。
1.3 二进制、八进制、十六进制快速互转
- 每 3 位二进制对应 1 位八进制;
- 每 4 位二进制对应 1 位十六进制;
- 分组从小数点向两边展开,不足位补 0。
例如:
十六进制只是二进制的紧凑写法,并没有改变底层比特。
2. 无符号数:所有位都贡献数值
位无符号数的范围是
例如 8 位无符号数的范围是 。加法只保留低 位,本质上是在模 的环上运算:
硬件得到 00000100;如果程序把它解释为普通自然数,就发生了无符号溢出。
3. 有符号整数:为什么最终选择补码
3.1 原码、反码、补码
设机器字长为 。对正数,三种编码相同;对负数:
- 原码:最高位表示符号,其余位表示绝对值;
- 反码:对应正数编码逐位取反;
- 补码:反码加 1。
8 位编码中, 与 为:
| 表示 | ||
|---|---|---|
| 原码 | 00000101 | 10000101 |
| 反码 | 00000101 | 11111010 |
| 补码 | 00000101 | 11111011 |
若总位宽为 :
| 编码 | 可表示范围 | 零的个数 |
|---|---|---|
| 原码 | 2 | |
| 反码 | 2 | |
| 补码 | 1 |
原码和反码都有 +0、-0 两个零,而且加减时需要单独处理符号。补码把正负数统一放进模 运算:
于是,同一套加法器也能做减法:
这就是硬件偏爱补码的根本原因,不只是“计算规则方便背”。
3.2 补码的范围不对称
位补码范围为
8 位补码范围是 。负数一侧多出来的 10000000 表示 ;它没有对应的 。所以对最小负数取相反数仍会溢出。
3.3 快速读补码
若最高位为 0,直接按无符号数读。若最高位为 1,可用两种方法:
- 逐位取反再加 1,所得绝对值前加负号;
- 最高位权看作 ,其余位权照常相加。
例如:
3.4 移码
移码给真值统一加一个偏置。若总位宽为 ,常用偏置 :
它让编码按无符号数从小到大排列时,真实值也从负到正递增,特别适合比较阶码。IEEE 754 的指数也采用偏置思想;单精度使用 127 而非 128,并把全 0、全 1 阶码留给特殊值。
4. 溢出与进位不是同一件事
无符号运算看最高位的进位;有符号补码运算看结果是否超出有符号范围。加法中,两个同号数相加却得到异号结果,就是有符号溢出。
对最高位的输入进位 与输出进位 ,也可写成
以 8 位为例:
01111111 (+127)
+00000001 (+1)
---------
10000000 (-128, 若按补码解释)
这里没有得到数学上的 ,而是发生有符号溢出。反过来,11111111 + 00000001 = 00000000 有最高位进位;按无符号数解释是溢出,按补码解释却是 ,没有有符号溢出。
5. 定点数:小数点位置由约定决定
定点数并不真的保存小数点,而是约定它在固定位置。若一个 位补码整数实际代表的值为 ,就称有 个小数位。
例如 8 位编码 01100000:
- 按整数解释是 ;
- 若约定 7 个小数位,则是 。
小数位越多,分辨率越高,但整数范围越小。这是固定总位宽下“范围与精度”的第一次权衡。
6. IEEE 754 单精度浮点数
32 位单精度分成三段:
| 字段 | 位数 | 含义 |
|---|---|---|
| 1 | 符号位 | |
| 8 | 带偏置的阶码,偏置为 127 | |
| 23 | 小数部分,规格化数默认有隐藏的前导 1 |
对 的规格化数:
6.1 编码示例:
因此:
- ;
- ;
- 。
最终编码为 0 10000110 01100100010000000000000,十六进制写作 0x43322000。
6.2 特殊值
| 阶码 | 尾数 | 含义 |
|---|---|---|
| 或 | ||
| 非 0 | 非规格化数,填补靠近 0 的范围 | |
| 任意 | 规格化数 | |
| 或 | ||
| 非 0 | NaN,不是一个数 |
非规格化数没有隐藏的前导 1,其值为
6.3 浮点数为什么会不精确
像十进制的 ,在二进制中是无限循环小数,只能截断到有限位。浮点数的间隔还会随数量级增大:同样是相邻可表示数,靠近 时的间距远大于靠近 1 时的间距。
因此浮点数运算要牢记:
- 表示的是附近的一个可表示数,不一定是原实数;
- 加法通常不满足结合律;
- 直接判断两个计算结果“完全相等”可能不稳妥;
- 位数固定后,范围和精度仍然互相牵制。
7. 非数值数据也是编码
课件还把逻辑值、字符和汉字列为非数值数据:
- 布尔值通常用 0、1 表示假和真;
- ASCII 用 7 位编码西文字符,例如字符
'0'的编码不是数值 0; - 汉字需要字符集与编码方案,编码本身不等于字形。
理解这一点很重要:内存里不存在“天然字符串”。只有当程序知道编码、长度和边界时,那串字节才会被解释成文本。
8. 一条统一的解题路线
面对任何“这一串比特是什么”的题,按顺序问:
- 位宽是多少? 位宽决定模和范围。
- 按什么类型解释? 无符号、补码、定点还是浮点。
- 小数点或字段边界在哪里? 定点位置、IEEE 754 的 S/E/M 都是约定。
- 运算是否截断? 硬件通常只保留固定宽度。
- 检查哪一种溢出? 无符号进位与有符号溢出不能混用。
这五问会一直贯穿后面的 MIPS 指令、ALU、存储器和 Cache。