前言

本文是 408 计算机组成原理的一轮考点强化笔记,依托 BOK408 计算机组成原理精讲课(25/26 版,24 年录制)整理,按「计算机系统概述 → 数据的表示与运算 → 存储系统 → 指令系统 → 中央处理器 → 总线 → 输入输出系统」七个板块推进。

笔记先从整机组成和性能指标建立全局框架,再依次讨论数据表示、运算部件、存储层次、指令系统和 CPU 执行机制,最后落到总线与输入输出。正文按考点顺序持续推进,后续内容随课程进度继续补充。

  1. 计算机系统概述:计算机硬件、软件、层次结构、工作原理与性能指标。
  2. 数据的表示与运算:数制与编码、符号扩展、定点数运算、运算部件、浮点数与 IEEE 754。
  3. 存储系统:存储器层次、多模块存储器、芯片扩展、Cache、虚拟存储器与外存。
  4. 指令系统:机器指令、寻址方式、程序机器级表示、CISC 与 RISC。
  5. 中央处理器:CPU 结构、指令执行、数据通路、微程序、流水线与多处理器。
  6. 总线:总线的基本概念、分类、性能指标与典型结构。
  7. 输入输出系统:异常与中断、I/O 接口、程序查询、中断、DMA 等方式。

一、计算机系统概述

这一部分不再按教材逐节展开,而是围绕几个贯穿全章的问题组织:

  1. 冯·诺依曼机靠什么把指令和数据组织成可执行的程序?
  2. CPU、主存和 I/O 设备分别负责什么,彼此通过什么连接?
  3. 一条指令是怎样被取出并执行的?
  4. 主频、CPI、MIPS 和执行时间之间是什么关系?

这几个问题既对应选择题里的基本概念,也是后面理解存储系统、指令系统和 CPU 的数据通路的基础。

考点1-冯·诺依曼机与“存储程序”

冯·诺依曼计算机由 运算器、控制器、存储器、输入设备和输出设备 五大部分组成。运算器和控制器合称中央处理器 CPU;随着集成电路发展,现代计算机又把运算器、控制器以及寄存器等集中在 CPU 中,但“五大部件”的划分仍然是理解整机结构的基本模型。

冯·诺依曼结构最核心的思想是存储程序:

  • 事先把程序以及程序需要处理的数据装入主存;
  • 指令和数据都以二进制形式存放;
  • CPU 从程序入口开始,按照地址逐条取指令、译码并执行;
  • 执行过程中,程序计数器 PC 指示下一条待取指令的位置。

从整机连接关系看,CPU 通过系统总线访问主存和 I/O 接口,外设再经过接口接入系统:


从主存里的二进制内容本身看,指令和数据没有天然区别,同一串位模式既可能被解释成指令,也可能被解释成整数、浮点数或地址。真正的区别来自 CPU 当前处于什么阶段、这个地址从哪里来:

  • 取指阶段,CPU 把 PC 给出的地址送到存储器,读出的内容作为指令送进指令寄存器 IR;
  • 译码阶段,控制器解释 IR 中的操作码,并据此判断这次操作需要哪些操作数;
  • 执行阶段,CPU 按指令给出的寻址方式或寄存器内容去访问数据。

例如,二进制序列 0000000000000001 既可能是整数 1,也可能是一条指令的编码。只有放到“取指—译码—执行”的控制过程中,它才获得确定含义。

因此,“指令和数据同等地位存储”强调的是:

  • 二者可以存放在同一个存储器中;
  • 二者都使用二进制表示;
  • 二者都通过相同的存储访问机制读取。

它并不意味着 CPU 无法区分二者。取指时由 PC 和控制器规定访问指令,执行时由指令中的地址字段规定访问数据。哈佛结构则把指令存储器和数据存储器分开,但 408 中讨论的一般仍是冯·诺依曼结构。


程序开始运行前,入口地址被送入 PC。之后循环执行:

  1. 取指:把 PC 中的地址送入 MAR,主存按地址读出内容并送入 MDR,再把 MDR 中的指令送入 IR;
  2. 更新 PC:PC 自动加上当前指令的长度,指向下一条指令;
  3. 译码:控制器分析 IR 中的操作码和地址字段;
  4. 执行:完成运算、访存、寄存器传送或转移等操作;
  5. 回到第 1 步,继续取下一条指令。

把取指阶段的寄存器传送写清楚,可以压缩成下面几行:

(PC) → MAR              // PC 给出下一条指令的地址
M(MAR) → MDR            // 主存读出指令
(MDR) → IR              // 当前指令进入指令寄存器
(PC) + 指令长度 → PC     // PC 指向下一条待取指令

这条循环可以表示成:


这里最容易混淆的是 PC 的“加 1”。在原理图或文字描述中经常写成

$$\mathrm{PC}\leftarrow \mathrm{PC}+1$$

但这里的“1”通常是一条指令所占的地址单元数,而不是字节数。若指令定长为 4 字节,主存按字节编址,则取完一条指令后应执行

$$\mathrm{PC}\leftarrow \mathrm{PC}+4$$

若主存按字编址,且一条指令正好占一个存储字,那么地址加 1 就确实表示取下一条指令。所以看到“PC+1”时,要先确认题目默认的编址单位和指令长度。

PC 的自动更新可以由 ALU 中的加法器完成,也可以由专用的 PC 增量器完成。原理上关心的是“PC 指向下一条待取指令”,具体用哪个硬件部件属于数据通路设计问题。转移指令、调用指令和中断响应则会在执行阶段直接改写 PC,因此“自动加 1”不是无条件覆盖 PC,而是取指阶段的默认动作。

自检:PC 到底怎么变化

题目:某机按字节编址,指令长度固定为 4 字节。PC 初值为 0x1000,取出这条指令后 PC 变成多少?

取指阶段要把整条 4 字节指令读走,因此 PC 应加 4:

$$\mathrm{PC}=0x1000+4=0x1004$$

若指令长度为 2 字节,则执行 PC←PC+2。只有在“按字编址且一条指令恰好占一个字”时,符号写法 PC←PC+1 才直接对应下一条指令。

记忆:PC 中存放的是下一条指令的地址,不是下一条数据的地址。操作数地址一般来自指令的地址字段、寄存器内容或寻址方式计算。

考点2-CPU、主存、I/O 与总线

408 题经常根据功能倒推寄存器名称,因此不能只记缩写:

部件 主要作用 易错点
PC 保存下一条待取指令的地址 它指向指令,执行阶段可能被转移类指令改写
IR 保存当前正在译码、执行的指令 保存整条指令,不只是操作码
MAR 保存本次要访问的存储单元地址 MAR 位数决定可寻址空间,不直接决定存储器总容量
MDR 保存与主存交换的数据或指令 又称 MBR,位数常与存储字长或数据总线宽度有关
通用寄存器 暂存操作数、中间结果、地址 程序员通常可见,由指令直接使用
PSW 保存标志位、中断允许位、运行状态等 其中条件码可能决定条件转移
ALU 完成算术运算和逻辑运算 ALU 本身不存放指令,也不单独决定执行顺序
控制器 对指令译码并产生控制信号 微程序控制器还会访问控制存储器

CPU 与主存之间一次典型的信息传递可以写成:

CPU 内部地址 → MAR → 主存
主存数据     → MDR → CPU 内部寄存器

PC 指向指令,IR 保存当前指令,MAR 送地址,MDR 接数据,四者不能因为都与“访存”有关而混用。


“主存”指 CPU 能直接按地址访问、用于存放当前运行程序和数据的那一层存储器。完整存储层次通常可以概括为:

  1. 寄存器:位于 CPU 内,速度最快、容量最小;
  2. Cache:位于 CPU 内或紧邻 CPU,对程序员透明,缓存主存中的活跃数据;
  3. 主存:存放当前要执行的指令和数据;
  4. 外存:如磁盘、SSD,容量大、价格低,但 CPU 不能直接执行其中的指令,程序和数据必须先调入主存。

上图展示的是完整存储层次,主存只是其中一层。寄存器也不全相同:既有指令可直接访问的通用寄存器,也有 PC、IR、MAR、MDR 这类专用寄存器。Cache 虽由高速存储单元构成,但逻辑上处于 CPU 与主存之间。


CPU 通常不能直接驱动键盘、磁盘等设备,中间需要 I/O 接口完成地址译码、数据缓冲、状态保存和格式转换。接口中常见三类寄存器:

  • 数据寄存器:暂存取入或送出的数据;
  • 状态寄存器:保存忙、就绪、错误等状态;
  • 控制寄存器:接收 CPU 发出的启动、读写模式等命令。

I/O 的主要控制方式包括程序查询、中断和 DMA。前两种通常仍由 CPU 参与数据传送,DMA 则让 DMA 控制器在设备与主存之间成批搬运数据,CPU 主要负责启动和收尾。

总线是一组能为多个部件共享的信息传输线。按所传信息可分为:

  • 数据总线:传送数据、指令和状态信息,宽度影响一次能传多少位;
  • 地址总线:传送存储单元或 I/O 端口地址,宽度决定可寻址范围;
  • 控制总线:传送读/写命令、中断请求、总线请求、时钟和应答等控制信号。

存储器和 I/O 设备都通过总线或相应的接口连接 CPU。现代计算机常采用多级总线结构,但无论结构怎样变化,“地址指出找谁,数据完成传输,控制信号规定何时做什么”这三类信息始终存在。

记忆:地址线决定“找哪里”,数据线负责“送什么”,控制线规定“何时、怎么做”。

考点3-按字节编址、按字编址与存储字长

这部分是系统概述与存储系统之间的连接点,也是后续计算主存容量、Cache 地址划分和指令地址的基础。

  • 按字节编址:每个地址对应 1 字节,地址加 1 表示移动到下一字节;
  • 按字编址:每个地址对应 1 个存储字,地址加 1 表示移动到下一个存储字;
  • 若一个存储字长 32 位,则每个存储字占 4 个字节。
编址方式 一个地址代表的数据 地址加 1 的含义 32 位地址的最大容量
按字节编址 1 字节 移动到下一字节 $2^{32}\,\mathrm{B}=4\,\mathrm{GB}$
按 32 位字编址 32 位,即 4 字节 移动到下一存储字 $2^{32}\times4\,\mathrm{B}=16\,\mathrm{GB}$

因此,“按字节编址且存储字长为 32 位”时,一个存储字会占用连续的 4 个字节地址。若某存储字从地址 A 开始,它的四个字节分别位于 A、A+1、A+2、A+3;下一条存储字从 A+4 开始。


存储字长是一个存储单元中实际存放的二进制位数,常见为 8、16、32 或 64 位。它不一定等于:

  • 指令字长:机器指令的位数;
  • 数据字长:CPU 一次处理的数据位数;
  • 数据总线宽度:一次总线传输的位数。

三者可能相同,也可能通过多次访存或总线传输完成匹配。例如存储字长为 32 位、数据总线宽度为 16 位时,一次读出或写入一个完整存储字可能需要两个总线传送周期。

先看一个地址对应多少数据,再数一共有多少个地址,最后相乘。不要看到 MAR 有 $n$ 位就直接写容量为 $2^n$ 字节。

若设 MAR 位数为 $n$,按字节编址,则最多有 $2^n$ 个字节地址,主存最大容量为

$$\text{容量}=2^n\,\mathrm{B}$$

如果按字编址、每个存储字为 $w$ 位,则

$$\text{容量}=2^n\times w\quad(\mathrm{bit})$$

例如 32 位地址、按字节编址时,地址空间上限是

$$2^{32}\,\mathrm{B}=4\,\mathrm{GB}$$

若同一机器改成“32 位地址、按 32 位字编址”,则可寻址 $2^{32}$ 个字,最大容量相当于 16 GB。地址位数没变,但每个地址所代表的数据量变了。

自检:编址单位改变后,容量变不变?

设地址线为 24 位:

  • 按字节编址:共有 $2^{24}$ 个字节地址,容量为

$$2^{24}\,\mathrm{B}=16\,\mathrm{MB}$$

  • 若按 16 位字编址:每个地址对应 2 字节,容量变为

$$2^{24}\times2\,\mathrm{B}=32\,\mathrm{MB}$$

地址位数完全没变,只是“一个地址代表多少数据”变了。题目若同时给出 MAR 位数和编址方式,必须按后者修正。

MAR 有 32 位并不自动等于存储容量 4 GB。必须先看编址单位;若按字编址且字长为 32 位,容量还要乘每个字的字节数。

考点4-主频、CPI、CPU时间与MIPS

主频 $f$ 表示 CPU 每秒钟产生多少个时钟周期,时钟周期 $T_c$ 是它的倒数:

$$T_c=\frac{1}{f}$$

例如主频为 $2\,\mathrm{GHz}$,则

$$T_c=\frac{1}{2\times10^9}=0.5\,\mathrm{ns}$$

频率使用十进制单位:$1\,\mathrm{kHz}=10^3\,\mathrm{Hz}$,$1\,\mathrm{MHz}=10^6\,\mathrm{Hz}$,$1\,\mathrm{GHz}=10^9\,\mathrm{Hz}$。存储容量中的 K、M、G 则经常使用 $2^{10}$、$2^{20}$、$2^{30}$,两类单位不要混用。


CPI 是 clock cycles per instruction,即平均执行一条指令所需的时钟周期数。若程序共执行 $N$ 条指令,平均 CPI 为 $\overline{\mathrm{CPI}}$,则总时钟周期数和执行时间分别为

$$C=N\times\overline{\mathrm{CPI}}$$

$$T_{\mathrm{CPU}}=N\times\overline{\mathrm{CPI}}\times T_c =\frac{N\times\overline{\mathrm{CPI}}}{f}$$

可以把这套关系压缩成一条“性能链”:

指令条数 × 平均 CPI = 所需时钟周期数
时钟周期数 × 时钟周期 = CPU 执行时间

CPU 时间 = N × CPI × Tc
         = N × CPI / f

不同指令的 CPI 可能不同,因此平均 CPI 必须按各类指令的条数加权:

$$\overline{\mathrm{CPI}} =\frac{\sum_i (N_i\times\mathrm{CPI}_i)}{N}$$

例如一个程序执行 10 亿条指令,平均 CPI 为 2.5,主频为 2 GHz,则

$$T_{\mathrm{CPU}} =10^9\times2.5\times\frac{1}{2\times10^9} =1.25\,\mathrm{s}$$

若只提高主频,但 CPI 因流水线加深、访存等待等原因上升,执行时间不一定下降。考试中比较“优化前后”,必须重新把指令条数、CPI 和主频三项一起代入。


MIPS 表示每秒执行多少百万条指令,即

$$\mathrm{MIPS} =\frac{\text{指令条数}}{\text{执行时间}\times10^6}$$

展开:从执行时间到 MIPS 的完整推导

把前面 CPU 时间公式 $T_{\mathrm{CPU}}=N\times\overline{\mathrm{CPI}}/f$ 代入:

$$\mathrm{MIPS} =\frac{N}{\dfrac{N\times\overline{\mathrm{CPI}}}{f}\times10^6} =\frac{f}{\overline{\mathrm{CPI}}\times10^6}$$

也可以先求每秒指令数 IPS:

$$\mathrm{IPS} =\frac{1}{T_{\mathrm{CPU}}/N} =\frac{f}{\overline{\mathrm{CPI}}}$$

再除以 $10^6$ 得到 MIPS。注意 $f$ 要使用 Hz,而不是 MHz 或 GHz;若 $f$ 取 GHz,则分母中的 $10^6$ 要相应换成 $10^3$。

以上式为例,2 GHz 主频、平均 CPI 为 2.5 时:

$$\mathrm{MIPS} =\frac{2\times10^9}{2.5\times10^6} =800$$

反过来,若已知 MIPS 和指令条数 $N$,程序执行时间为

$$T=\frac{N}{\mathrm{MIPS}\times10^6}$$


两台机器的指令集不同,完成同一个任务所需的指令条数可能相差很多;同一条高级语言语句也可能编译成不同数量的机器指令。因此 MIPS 只能在指令集、编译器以及程序特征大致相同时作粗略参考,比较计算机性能的核心指标仍是 完成同一任务所需的执行时间。

主频提高只缩短了 $T_c$。如果优化导致程序指令数增加,或流水线加深、访存等待使平均 CPI 上升,最终执行时间仍可能变长。比较性能时必须把三个量一起代入。

若题目讨论流水线、访存缺失、Cache 等情况,所谓实际 CPI 还应包含这些停顿周期。可以把总周期拆为

$$C=N\times\mathrm{CPI}_{\text{ideal}}+ \text{停顿周期}$$

再用总周期乘时钟周期求执行时间。这一思路会在 CPU 流水线部分继续使用。

本部分常见易错点

例如“指令和数据完全无法区分”“主频越高运行越快”“MIPS 越大机器性能一定越强”,这些说法都忽略了控制过程和应用条件。

易混内容 正确理解
指令和数据是否“完全无法区分” 二进制形式相同,但按 PC 取指还是按地址取操作数,由控制过程和地址来源决定
PC 是否指向数据 PC 通常指向下一条指令;操作数地址来自指令或寄存器
PC+1 是否一定是加 1 字节 取决于编址单位和指令长度,原理图中的“1”常表示一条指令或一个存储字
MAR 位数是否直接等于容量 还要看编址单位和每个地址对应的数据宽度
存储字长是否等于指令字长 可以不同,要通过多次访存等方式匹配
主频越高是否一定越快 还要看总指令数和 CPI,不能单独比较主频
MIPS 是否可跨机器绝对比较 不适合跨指令集直接比较,只反映单位时间执行的指令条数
一分钟复盘:四个必须能脱口而出的结论
  1. 存储程序:程序和数据先放入主存,CPU 按 PC 逐条取指执行。
  2. 指令与数据:位模式本身无标签,由取指/执行阶段和地址来源决定解释方式。
  3. 存储容量:地址数 × 每个地址对应的数据宽度,不能只看 MAR 位数。
  4. 执行时间:$T_{\mathrm{CPU}}=N\times\mathrm{CPI}\times T_c$;MIPS 只是间接指标。

二、数值的表示与运算

考点1-进位计数制及其转换

数值表示题最常见的错误,不是不会算,而是把不同层次的概念混在一起比较。先看一个总框架:

  • 按“有没有符号”先分成无符号数和有符号数;
  • 无符号数直接按二进制位权解释,常用于地址、计数和边界检查;
  • 有符号数才需要进一步指定原码、反码、补码或移码;
  • 同一个位模式在不同编码下可能得到完全不同的真值。

因此,“原码”和“无符号数”并不是同一层的分类:无符号数是一类解释方式,原码是带符号数的一种具体编码。


机器数是在机器中实际存放的固定位宽位模式,真值是这个位模式按照某种编码解释后得到的数学意义上的数。

两个概念不能用“二进制/十进制”来区分:

  • 机器数除了写成二进制,也常写成十六进制或一串位;
  • 真值不一定写成十进制,也可以写成 $+1100_2$、$-101_2$;
  • 真值的标志是用 $+$、$-$ 表示正负,而机器数用最高位等规则编码符号。

同一个 4 位机器数 1100 可以表示:

解释方式 真值 计算过程
无符号数 $12$ $8+4$
原码 $-4$ 符号位为 1,数值位 100 表示 4
反码 $-3$ 负数反码的数值位取反得 100,表示 3
补码 $-4$ $-2^3+2^2=-8+4$
4 位移码(偏置 8) $4$ 无符号值 $12-8=4$

所以,单说“1100 等于多少”是不完整的。必须先问:它按无符号数还是某种有符号编码解释?位宽是多少?

注:真值 +1100 与 4 位机器数 1100 看起来字符相同,但前者已经把正号单独写出来,是一个带符号真值;后者仍只是一个需要指定编码才能解释的位模式。


无符号数没有符号位,所有位都按权值相加:

$$V=\sum_{i=0}^{n-1}b_i2^i$$

例如 8 位无符号数 10110100:

$$V=128+32+16+4=180$$

无符号数的 $n$ 位表示范围是

$$0\sim 2^n-1$$

地址通常是非负整数,而且要覆盖整个地址空间,因此常按无符号数处理。例如比较两个地址大小时使用无符号比较;若误把它们当成有符号补码,地址最高位为 1 时就会被判成负数。


原码的规则最直接:最高位表示符号,其余位直接表示绝对值。

  • 正数:符号位为 0,数值位就是真值的二进制;
  • 负数:符号位为 1,数值位仍写绝对值的二进制;
  • 由原码求真值:去掉符号位,把数值位直接转成十进制,再加上符号。

例如:

$$[+5]_{\text{原}}=0101 \qquad [-5]_{\text{原}}=1101$$

原码的优点是符号和绝对值一目了然,缺点是加法和减法不能直接统一成补码加法,而且零有两种表示:

$$[+0]_{\text{原}}=0000 \qquad [-0]_{\text{原}}=1000$$


反码由原码得到:

  • 正数的反码与原码相同;
  • 负数保持符号位不变,数值位逐位取反;
  • 由负数反码求真值时,把数值位再取反得到绝对值的二进制。

例如:

$$[-5]_{\text{原}}=1101 \qquad [-5]_{\text{反}}=1010$$

反码同样有正零和负零,实际运算中主要用于理解它与补码之间的关系。


补码是现代计算机中最常用的定点整数编码。正数补码与原码相同;负数补码可由原码“数值位取反再加 1”得到,也可以由反码末位加 1 得到。

更统一的理解是最高位带负权。对于 $n$ 位补码 $b_{n-1}\cdots b_1b_0$:

$$V=-b_{n-1}2^{n-1}+\sum_{i=0}^{n-2}b_i2^i$$

例如:

$$1100_{\text{补}} =-2^3+2^2 =-8+4 =-4$$

最高位为 1 时,先写下最高位的负权值,再把其余位按普通无符号数相加。比如

$$11101010_{\text{补}}=-128+64+32+8+2=-22$$

这比“先取反、再加 1、再按无符号数读”更不容易丢步骤。

补码求相反数也很方便:把补码连同符号位一起逐位取反,再在末位加 1:

$$[-X]_{\text{补}}=\sim[X]_{\text{补}}+1$$

所以两个补码相减可以直接改成“加相反数”:

$$[X-Y]_{\text{补}}=[X]_{\text{补}}+[-Y]_{\text{补}}$$

这正是补码适合统一实现加减法的原因。


符号扩展不会改变真值。 把较短补码放入更宽寄存器时:

  • 正数高位补 0;
  • 负数高位补 1;
  • 扩展前后补码表示的真值完全相同。

例如 4 位补码 1100 表示 $-4$,扩展到 8 位后是

$$11111100_{\text{补}}=-128+64+32+16+8+4=-4$$

反过来说,一个很长的补码如果高位有大量重复的符号位,可以化简为能保持真值的最短补码:

$$111111111100_{\text{补}} \rightarrow 1100_{\text{补}}=-4$$

自检:长补码化简后为什么值不变?

短补码 1100 表示 $-4$。每次把它符号扩展到左边一位,相当于新增一个权值为 $+2^k$ 的位,同时原来负权最高位从 $-2^k$ 变成更高一层的负权:

$$-2^{k+1}+2^k=-2^k$$

新增的 1 与符号扩展造成的权值变化恰好抵消,所以真值不变。

这就是“连续相同的符号位可以删到最短”的原因,但最低有效位和符号位不能随意删。


移码通常用“真值 + 偏置”得到编码:

$$[X]_{\text{移}}=X+\text{偏置}$$

对普通 $n$ 位整数移码,常用偏置是

$$2^{n-1}$$

例如 4 位移码的偏置为 8:

真值 移码
$-8$ 0000
$0$ 1000
$7$ 1111

把移码当无符号数看,其大小顺序与真值顺序一致,因此浮点数常用来表示阶码,便于直接比较大小。

同一真值的移码与补码相比,符号位相反,数值位相同。例如 4 位补码与移码:

真值 补码 移码
$-4$ 1100 0100
$0$ 0000 1000
$3$ 0011 1011

普通 $n$ 位整数移码的偏置是 $2^{n-1}$。

IEEE 754 的阶码字段有 $k$ 位时,偏置是

$$2^{k-1}-1$$

例如单精度阶码有 8 位,偏置是 127,而不是 128。IEEE 754 还需要把全 0 和全 1 阶码留给零、非规格化数、无穷大和 NaN,因此规格化数的阶码字段只用 $1\sim254$,对应实际指数 $-126\sim127$。


设机器字长为 $n$,并把这些编码按定点整数理解,则固定位数真值的上下限为:

编码 $n$ 位真值范围 8 位范围 零的表示
无符号数 $0\sim2^n-1$ $0\sim255$ 唯一
原码 $-(2^{n-1}-1)\sim2^{n-1}-1$ $-127\sim127$ 正零、负零
反码 $-(2^{n-1}-1)\sim2^{n-1}-1$ $-127\sim127$ 正零、负零
补码 $-2^{n-1}\sim2^{n-1}-1$ $-128\sim127$ 唯一
移码 $-2^{n-1}\sim2^{n-1}-1$ $-128\sim127$ 唯一

补码比原码、反码多表示一个负数,是因为 1000...0 不再浪费在负零上,而被规定为 $-2^{n-1}$。

自检:说明 4 位补码 `1000` 为什么表示 $-8$ 而不是 $-0$

按最高位负权计算:

$$1000_{\text{补}}=-2^3=-8$$

如果按“符号位 + 绝对值”理解,就会误以为它是负零。原码、反码和补码必须使用各自的解释规则,不能把原码规则套到补码上。

同理,8 位补码的表示范围是 $-128\sim127$;10000000 表示 $-128$,它的模 8 位相反数仍然是自身。

如果题目表示的是定点小数,而不是整数,则范围随隐含小数点位置缩放。若低 $k$ 位是小数位,实际真值要整体乘 $2^{-k}$,表示间隔和范围也同步缩放。


移位的题目要先问两个问题:操作对象是哪一个位模式,右移时高位该补什么。

对于补码:

  • 算术左移:低位补 0,高位直接移出;
  • 算术右移:高位补原来的符号位;
  • 逻辑右移:高位一律补 0,不保留符号。

逻辑左移也是低位补 0、高位移出,但“逻辑”与“算术”的差别主要体现在右移:逻辑右移把整个位模式看成一串二进制位,算术右移则把最高位解释成负权。

补码 1101    算术右移 1 位 → 1110
             (-3)          (-2)

补码 1101    逻辑右移 1 位 → 0110
           (若按无符号看)   (13 → 6)

算术右移相当于对补码做“除以 2 后向负无穷取整”。所以负数右移时可能看起来只变了 1:

$$-3\gg 1=-2$$

逻辑移位通常用于无符号数,算术移位用于补码。若把负数补码直接做逻辑右移,符号位会被清成 0,真值语义随即被破坏。

左移 1 位时,可以直接观察移位前后的符号位:

  • 符号位没有改变,通常没有有符号溢出;
  • 符号位改变,发生有符号溢出。

例如 4 位补码中:

$$0101_2=5 \quad\xrightarrow{\text{算术左移 1 位}}\quad 1010_2=-6$$

从正数移成了负数,说明 $5\times2=10$ 已超出 4 位补码范围,发生溢出。若结果是 $1100\rightarrow1000$,二者仍都是负数;$-4\times2=-8$ 恰好在 4 位补码范围内,所以不溢出。

左移相当于乘 2,右移相当于除 2 的幂;但只有在目标结果仍能由当前位宽表示时,移位后的位模式和数学结果才一致。

考点2-补码加减运算与加法器

补码加减法的核心是:减法先改成加相反数,剩下的全部交给加法器。

$$X-Y=X+\overline{Y}+1$$

下面这张图把“加法”和“减法”统一到了同一套硬件:

带标志位的补码加/减法器:Sub 控制第二操作数是否取反并决定最低位进位,上方输出结果 F、最高位进位 Cout 和四个标志位

读图时抓住三个信号:

  1. 左输入 $X$ 直接进入加法器;
  2. 右输入 $Y$ 先经过多路选择器;
  3. 控制信号 Sub 同时控制“是否取反”和“最低位进位”。

具体工作过程如下:

Sub 第二操作数 最低位进位 $C_0$ 加法器实际完成
0 $Y$ 0 $F=X+Y$
1 $\overline{Y}$ 1 $F=X+\overline{Y}+1=X-Y$

最低位进位 $C_0$ 就是图中右侧从 Sub 引来的一根控制线。做减法时如果没有把 $C_0$ 置 1,电路实际计算的是“反码相加”,结果会与补码差值相差 1。

8421 口诀:加法 0 与 0,减法 1 与 1。Sub=0 时第二操作数不取反、进位为 0;Sub=1 时取反、进位为 1。

加法器输出的结果只保留低 $n$ 位,更高位的进位会单独成为 $C_{\text{out}}$。因此它天然服从 $2^n$ 取模运算:

$$F=(X\pm Y)\bmod 2^n$$

是否把这个结果解释为有效真值,要看后面四类标志位。


图中顶部的四个标志位分别回答四个不同问题:

标志 名称 由图中的哪个输出得到 含义
ZF 零标志 所有结果位都为 0 结果是否为 0
SF 符号标志 结果最高位 $F_{n-1}$ 按补码解释时结果为正还是负
CF 进位/借位标志 最高位进位 $C_{\text{out}}$ 结合 Sub 无符号数运算是否越界
OF 溢出标志 符号位进位与符号位输入进位 有符号补码运算是否越界

设最高位为第 $n-1$ 位:

  • $C_n$ 表示加法器最终向外产生的进位,也就是图中的 Cout;
  • $C_{n-1}$ 表示由低一位送进符号位的进位,也就是“符号位下面的进位”;
  • $F_{n-1}$ 是结果的最高位。

四个标志可写成:

$$ZF=1\iff F=000\cdots0$$

$$SF=F_{n-1}$$

$$OF=C_n\oplus C_{n-1}$$

$$CF= \begin{cases} C_n, & \text{加法 }(\mathrm{Sub}=0)\\[4pt] \overline{C_n}, & \text{减法 }(\mathrm{Sub}=1) \end{cases} $$

也可把 CF 合并写成

$$CF=C_n\oplus\mathrm{Sub}$$

CF 面向无符号数,表示加法有进位或减法不够减需要借位;OF 面向有符号补码,表示正负号组合已经超出当前位宽。

同一次运算完全可能“CF=1、OF=0”,也可能“CF=0、OF=1”。


补码溢出有三种等价判断法,分别适合不同题型:

方法 观察对象 判断规则
符号法 两个操作数和结果的符号 正 + 正得负,上溢;负 + 负得正,下溢
进位法 符号位进位 $C_{n-1}$ 与最终进位 $C_n$ 两者不同则溢出:$V=C_n\oplus C_{n-1}$
双符号位法 两个符号位 $S_1S_2$ 结果双符号位不同则溢出:$V=S_1\oplus S_2$

设 $A_s,B_s,F_s$ 分别是两个加数和结果的符号位,则单符号位法的逻辑表达式为

$$V=A_sB_s\overline{F_s} +\overline{A_s}\overline{B_s}F_s$$

其中 $V=1$ 表示溢出。第一项对应“负 + 负 = 正”,第二项对应“正 + 正 = 负”。

双符号位法可以先对操作数做符号扩展,使符号位变成两位,再参加运算:

  • 正数变成 00...;
  • 负数变成 11...;
  • 结果的两个符号位相同 → 未溢出;
  • 结果的两个符号位不同 → 溢出。

例如 4 位操作数扩展成 5 位双符号位:

  00111      7 + 1 = 8
+ 00001
-------
  01000
   ↑↑
   01:双符号位不同,发生上溢
自检:为什么同一组结果可能 CF=1、OF=0?

以 4 位运算为例子。

(1)1111 + 0001 = 0000

  • 无符号数:$15+1=16$,超出 4 位无符号数范围,CF=1;
  • 补码:$-1+1=0$,结果完全正确,OF=0;
  • 结果最高位为 0,所以 SF=0、ZF=1。

(2)0111 + 0001 = 1000

  • 无符号数:$7+1=8$,没有超过 4 位无符号数范围,CF=0;
  • 补码:$7+1=8$ 已超出 $-8\sim7$,OF=1;
  • 结果的符号位为 1,所以 SF=1,但零标志 ZF=0。

这两个例子说明:CF 与 OF 服务的是两套不同的数值解释。


加法器的速度瓶颈在于进位传递方式。

串行进位加法器把 $n$ 个全加器首尾相连,每一级的进位都等待前一级算完:

FA₀ ──C₁──> FA₁ ──C₂──> FA₂ ──> … ──Cₙ──> FAₙ₋₁

结构简单、硬件少,但最坏情况下进位要从最低位一路传到最高位,延迟随位数近似线性增长。

并行进位加法器也称先行进位加法器。它增加专门的进位逻辑,使各高位进位不等前一级逐级算完,而是尽可能同时产生。

因此,并行进位的主要优点是缩短进位延迟,代价是门电路和连线更多、结构更复杂。408 中掌握这层区别即可,不需要展开具体进位公式。

参考来源