← 计算与系统基础 · 第 1 篇

一条加法指令怎样在计算机里执行

用 8 位模型区分无符号进位与有符号溢出,连接位表示、加法器、指令语义和 CPU 执行过程。

学完这一篇,你能做到

分别解释位串结果、进位和有符号溢出,并说明教学模型与真实 CPU 的差别。

计算机里的“加一”有几层含义:程序表达一个计算,指令集规定机器可见的结果,处理器电路完成状态变化。把这几层分开,就能解释为什么同样是加法,Python 可以得到 256,一个 8 位教学模型却会得到 0。

这里先用固定宽度的整数讲清进位和溢出,再把它放回一条指令的执行过程。示例是离线的 8 位计算模型,不是 RISC-V 模拟器,也不描述某颗 CPU 的实际电路和时序。

位怎样组成整数

一个 bit 有两种状态。n 位无符号整数可以表示 0 到 2ⁿ−1,8 位的范围是 0 到 255。最高位权重为 128,最低位权重为 1;相同的位串按不同解释规则读取,数值也会不同。

8 位二进制补码整数的范围是 −128 到 127。位串 10000000 作为无符号数是 128,作为补码有符号数是 −128。有符号与无符号不是“电路里有两套数字”,而是程序和指令如何解释这些位。

固定宽度只保留低 n 位,相当于结果对 2ⁿ 取模。这个规律解释了回绕,不代表业务上允许回绕。例如余额、库存或数组长度溢出时,通常应该拒绝、扩展表示范围或触发明确错误。

从布尔运算到加法器

一位全加器读取两个输入位和一个输入进位,输出和位与下一位的进位。XOR 用于表示位的不同,AND 与 OR 可以组合出进位条件。把这些小单元连接起来,就能构造逐位传递进位的加法模型。

这种连接称为一种串行进位思路,但真实硬件可能采用更复杂的进位结构。晶体管实现逻辑门,逻辑门构成组合电路,寄存器等存储单元保存状态。不要把软件里的 a + b 想成处理器一定逐位调用了八次函数。

时钟用于协调某些状态更新,并不意味着每条指令都恰好一个时钟完成。不同处理器、缓存命中、指令依赖和执行设计会改变工作过程;没有具体型号与测量条件,不能给出通用周期数。

进位和有符号溢出是两个问题

255 + 1 的完整结果是 256。8 位只保留低位后得到 0,最高位之外有进位。把两个输入按补码理解,它们是 −1 和 1,相加为 0,没有有符号溢出。

127 + 1 的低 8 位是 10000000。无符号结果 128 仍在范围内,没有向第九位进位;按补码解释却是 −128。两个非负数加出负值,说明有符号结果超出了可表示范围。

因此,检查 结果 < 输入 之类的经验规则前,必须说明位宽、输入解释和操作语义。不同语言还可能规定截断、异常、任意精度或其他行为,不能只根据数学公式推断程序行为。

一条指令规定什么

指令集架构(ISA)规定指令编码、寄存器和操作的可见语义。以 RISC-V 的基础整数指令为例,RV32I 的整数寄存器宽度为 32 位;ADD 保留结果低 XLEN 位,算术溢出不会由这条指令自动产生异常。不要从这里的进位检查推断 RISC-V 存在通用算术标志寄存器。

可以把执行过程概念性地理解为:按程序计数器取得指令、解码、读取源寄存器、完成运算、写入目标寄存器并决定下一条指令的位置。加载数据到寄存器与在寄存器间相加是不同操作。

真实 CPU 可能流水执行、预测分支或在约束下调整内部执行顺序。ISA 的可见行为与内部实现不是同一层。缓存也服务于数据访问,但不改变加法应产生的架构结果。性能分析需要具体硬件与程序证据。

在 Python 中显式模拟 8 位结果

Python 整数不受这里的 8 位限制,所以示例先计算完整的 total,再用 & 255 保留低八位。carry 检查完整结果是否超过无符号范围。overflow 检查两个输入符号相同、结果符号不同的条件。

def add8(a, b):
    assert 0 <= a < 256 and 0 <= b < 256
    total = a + b
    value = total & 255
    carry = total > 255
    overflow = bool((~(a ^ b) & (a ^ value)) & 128)
    return value, carry, overflow

assert add8(255, 1) == (0, True, False)
assert add8(127, 1) == (128, False, True)
assert add8(1, 2) == (3, False, False)
print(add8(255, 1), add8(127, 1))

输出是 (0, True, False) (128, False, True)。返回值中的整数是无符号位串解释,第三项则针对把相同位串解释成补码整数的加法。代码没有设置处理器标志,也没有读取真实寄存器。

这里的断言检查示例输入;应用接口不能依赖 assert 作为唯一参数校验,因为 Python 优化运行可以移除断言。真实边界应显式检查输入类型、范围并返回定义好的错误。

练习:把解释规则也写出来

增加一个把 8 位值解释成补码整数的函数:最高位未置位时直接返回原值,置位时返回 value − 256。分别展示 255 + 1 与 127 + 1 的无符号解释和有符号解释。

验收时检查 0、127、128、255 的转换,以及不溢出的正数相加、负数相加、异号相加和有符号越界。每个测试同时说明位串结果、进位与溢出,避免用一个“出错”标签混淆三种含义。

再将位宽改成参数 n,推导掩码 2ⁿ−1 和符号位 2ⁿ⁻¹。保持输入范围检查,并明确模拟的是固定宽度整数语义。它仍不会说明真实加法器的功耗、速度或 CPU 完整执行行为。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。