计算机组成原理课后习题精析:CPU性能与数据表示机制 简介计算机组成与系统结构袁春风·第三版课后习题答案汇总面向计算机专业本科生、考研复习者及自学计组、指令系统与汇编语言的人。内容紧密围绕性能评估、MIPS、CPI、时钟频率、性价比分析等高频考点通过典型习题给出逐步推导与参考答案文档用多组机器对比、指令条数与执行时间表格演示了机器选型、平均时钟周期数、执行时间占比等题型的规范解法并对易混淆的结论如速度提升百分比和CPI比例做了辨析。资源为一篇doc文档压缩包共1个文件、约1.75MB按章节整理收录第一、二章等关键习题并给出答案要点便于配合教材边学边对照、查漏补缺。已有1190人学习下载既适合课后消化例题也适合考前快速梳理重点计算题型。整体是一份紧凑、可复用的计组习题答案解析能帮助读者在较短时间内掌握性能计算类题目的通用思路。1. 计组课后习题不是背公式为什么指令条数少的程序反而跑得慢计组这门课最容易让人栽跟头的地方不是公式记不住而是做题全靠直觉。第三章有一道题很反直觉同一台机器上指令序列 S2 只有 8 条指令另一个序列 S1 有 10 条结果 S1 跑完只要 38nsS2 却要花 52ns。指令少的反而慢。这个现象背后正好是计算机组成与系统结构里性能分析的灵魂程序执行时间等于指令条数、CPI、时钟周期三者的乘积。三个变量互相牵制只看其中一个结论就必然翻车。这份课后习题答案汇总的价值也在这里它把基准测试、数据表示、运算机制、校验原理全部落成了可验证的数字适合一题一题推演而不是把参考答案背下来。搞懂这些题等于把指令系统底层的设计逻辑重新走了一遍。2. 性能标尺CPU时间、MIPS与性价比如何决定机器选型2.1 CPU执行时间的三因子分解任何程序的 CPU 执行时间都可以写成CPU 时间 指令条数 × CPI × 时钟周期。这里 CPI 是每条指令的平均时钟周期数时钟周期是主频的倒数。三个因子中任何一个变化执行时间都会跟着变。回到习题 9。编译器把同一段高级语言代码生成两个指令序列 S1 和 S2机器主频都是 500MHz。S1 的 10 条指令里5 条 CPI1、2 条 CPI2、2 条 CPI3、1 条 CPI4算下来 CPI 是 1.9S2 有 8 条指令但 5 条是 CPI4 的 D 类指令CPI 高达 3.25。各自执行时间S110 × 1.9 / 500MHz 38nsS28 × 3.25 / 500MHz 52nsS2 指令少了 20%但 CPI 涨了 71%最终执行时间反而长了 36.8%。习题答案后面补了一句很关键的话并不是指令条数少的目标程序执行时间就一定少。这句话值得刻在脑子里。这个公式最大的用途是把“快慢”这种模糊感受拆成可定位的变量。机器慢可以先看主频是不是低再看程序是不是用了大量高 CPI 的指令最后才看指令条数。很多人调性能一上来就数指令方向就错了。2.2 MIPS峰值与实际的差距MIPS 是每秒百万条指令计算公式是指令条数除以执行时间再除以 10^6。习题 5 给了两台机器跑两个基准程序的数据。看 M1 和 M2 跑 P2 的结果M1 是 300×10^3 条指令用 3ms算出来 100MIPSM2 是 420×10^3 条指令用 6ms算出来 70MIPS。从 MIPS 看M1 比 M2 快 1.43 倍。但注意M1 和 M2 的价格分别是 5000 元和 8000 元如果只比 MIPS 就草率选 M1后面性价比那步就会算错。更迷惑的是峰值 MIPS。习题 6 里M1 主频 1GHzM2 主频 1.5GHz但 M2 的各类指令 CPI 都更高。M1 找一段全是 A 类指令的程序CPI1峰值 1000MIPSM2 即使用最快的 A、B 两类指令因为 B 类 CPI2峰值也只有 750MIPS。主频更高的机器峰值 MIPS 反而更低因为峰值 MIPS 只反映“这台机器理论上最快能跑多快”而现实中程序必然混合使用各种指令真实 CPI 一定比最理想情况高。2.3 性价比算术平均与几何平均谁说了算习题 5 的第四问和第五问是整套答案里设计得很妙的题目。用户大量使用 P1关心响应时间性价比公式定义为 R 1 /执行时间 × 价格。M1 跑 P1 要 10msM2 只要 5ms但 M1 便宜 3000 元。算“执行时间 × 价格”M1 是 10 × 5000 50000M2 是 5 × 8000 40000。值越小性价比越高所以选 M2。第五问变成两个程序同等使用结果就复杂了。用算术平均还是 M2 胜出但用几何平均sqrt(10 × 0.003) × 5000 小于 sqrt(5 × 0.006) × 8000结论反过来了应该选 M1。同一批数据两种平均方法给出完全相反的选型建议。算术平均把执行时间大的 P1 的差异直接放大几何平均则削弱了量纲差异带来的影响。实际做性能选型时选择哪种平均方式取决于负载特征——如果耗时集中在单个程序算术平均更符合直觉如果是大量同质任务的综合吞吐几何平均更能反映真实的感知比。3. 数据表示补码、移码与IEEE 754的底层逻辑3.1 补码为什么减法能变成加法习题 4 列了一组 8 位定点小数要求写原码和补码。比如 -0.1001 的原码是 1.1001000补码是 1.0111000。补码的定义是负数取反加一但这样定义的原因是为了让减法统一成加法。CPU 里的加法器只要实现加法减法通过“补码加一”转换为加法硬件就能少做一套减法电路。更有意思的是边界值。同样的 8 位格式1.0 在原码和补码里都溢出因为 7 位数值位最大只能表示 0.1111111不到 1但 -1.0 的补码 1.0000000 却能表示。这是补码特有的不对称性定点小数的补码表示范围是 -1 到 (1-2^-7)负数端比正数端多一个点。习题 6 里给了 [x] 补 10000000求 x答案是 -128也是这个道理。补码把“0”唯一化了原码里 0 和 -0 都在补码里 10000000 不再表示 -0而是整个范围最左边的那个负数。3.2 移码与偏置为什么指数要加127习题 5 让写补码和移码。1001 的补码是 00001001移码是 10001001-1001 的补码是 11110111移码是 01110111。对比就能看出来移码和补码的区别只在符号位补码的符号位取反就是移码。移码的核心设计动机是让阶码可以按无符号数比较大小。浮点数做加减法时要先对阶比较两个指数的大小。如果指数用补码表示比较大的负数在二进制序里会排在后面比较电路就要额外处理符号。用了偏置常数后指数加上 127或题目里的 16、128最小指数变成全 0最大指数变成全 1大小顺序和二进制序完全一致。IEEE 754 选 127 是因为单精度阶码 8 位能表示 -126 到 127全 0 和全 1 另有用途加上 127 后映射到 1 到 254正好避开两个保留的边界值。3.3 IEEE 754的隐藏位与渐进下溢习题 7 是这份答案里最值得反复看的一道题。R1 内容 0000108BHR2 内容 8080108BH三条指令把它们当作无符号数、带符号整数、单精度浮点数真值完全不同。作为浮点数R1 的阶码全 0是非规格化数没有隐藏的 1尾数是 0.002116H指数固定为 -126真值是 0.002116H × 2^-126R2 的阶码是 00000001规格化数尾数里有隐藏的 1指数的真值是 1 - 127 -126结果是 -1.002116H × 2^-126。同一个位模式符号位和阶码差一个 1规格化状态就完全不同。规格化数隐藏位的设计很精妙规格化数尾数最高位必然是 1那就干脆不存省下的那一位多存一个有效位。但代价是 0 无法用规格化表示所以阶码全 0 时改走非规格化路径。习题 15 的范围表里最小非规格化数是 2^-23 × 2^-126 2^-149而最小规格化数是 1.0 × 2^-126两者之间是连续的、平滑逼近 0 的而不是直接掉到 0。这种“渐进下溢”避免了数值从 2^-126 突然跳成 0 的精度断层。3.4 大端小端同一个数在不同内存布局下的机器数习题 17 让画出 x-0.125、y7.5、i100 在大小端机器上的内存分布。先算出机器数-0.125 是 BE000000H7.5 是 40F00000H100 是 0064H。大端机把最高字节放在最低地址地址 100 处是 BEH小端机把最低字节放在最低地址地址 100 处是 00H。这题的陷阱在于很多人以为“大小端只是字节序颠倒”实际上浮点数的符号位、阶码、尾数在内存里也跟着一起按字节倒置。x 在地址 100~103在小端机上就是 00H 00H 00H BEH连符号位所在的字节都被搬到了最后。网络协议用大端x86 用端小端跨端传数据时如果直接读内存字节符号位、指数位全部错位这就是为什么序列化要用明确的字节序约定。4. 运算机制类型转换、移位、乘除优化与检错纠错4.1 unsigned的len-1一个循环灾难的根因习题 9 的 C 程序参数 len 是 unsigned 类型循环条件是 i len-1。len 为 0 时程序没有返回 0而是触发存储器访问异常。float sum_elements(float a[], unsigned len) { int i; float result 0; for (i 0; i len-1; i) result a[i]; return result; }根因是 unsigned 的溢出行为。len0 时len-1 不是 -1而是 0xFFFFFFFF即 unsigned 能表示的最大值。任何有符号的 i 和它比较时i 先被隐式转换成 unsigned于是 i 永远小于等于 0xFFFFFFFF循环体一直执行a[i] 越界访问。修复方法有两个把 len 声明成 int或者把测试条件改成 i len。后者更符合直觉因为 i len 在 len0 时直接不进入循环。这类问题在真实代码里很常见尤其是把接口里的长度参数写成 unsigned 又做了 -1 运算的。C 语言的隐式类型转换规则决定了有符号和无符号混合比较时有符号会转成无符号负数的真值瞬间变成几十亿。4.2 逻辑移位与算术移位func1和func2为什么结果不同习题 3 的两个函数看起来只差一个括号行为却有本质区别。int func1(unsigned word) { return (int) ((word 24) 24); } int func2(unsigned word) { return ((int) word 24) 24; }func1 全程按无符号数处理左移 24 位再逻辑右移 24 位相当于把高 24 位清零结果必然是非负的。func2 先把 word 转成 int左移 24 位后做的是算术右移右移补的是符号位。所以 w0000 0080H 时func1 返回 128func2 先左移得到 80000000H符号位是 1算术右移后得到 FFFFFF80H对应 -128。而 w0000 0100H 时左移 24 位把唯一的 1 移出了 32 位结果是 0。这里值得停下来想一层为什么带符号整数必须用算术右移因为补码的负数右移时高位补 0 会让负数变成正数数值完全错乱。算术右移保持符号位相当于对负数做向下取整的除以 2 的幂。代价是右移对负数不是向零取整这正好引出下一节的除法优化。4.3 编译器优化乘法如何变成移位与加减习题 5 给了一段反编译得到的机器代码要从它反推出原函数里的常量 M 和 N。int optarith(int x, int y) { int t x; x 4; x - t; if (y 0) y 3; y 2; return x y; }x4 是乘 16再减掉原来的 x就是 x×15所以 M15。y2 是除以 4但算术右移对负数向负无穷取整而 C 语言整数除法向零取整所以先给负数加上一个偏置量。y0 时 y3加上 3 之后右移 2 位等价于向零取整的除以 4。为什么要加 3因为 2^2 减 1。对任意负数 y右移 2 位是 floor(y/4)要向零取整就得先把负数垫高 3使得 floor((y3)/4) trunc(y/4)。如果换成偏置 2y-3 时 -32-1右移两位还是 -1但 -3/4 向零取整是 0就错了。这道题同时也是指令系统的缩影高级语言里的乘除编译成一堆移位、加减、条件分支而不是真的调用乘法指令。机器不提供乘除指令程序照样能跑只是生成的指令序列更长执行更慢。4.4 传输可靠性奇偶校验、SEC与CRC怎么工作数据在总线上传输时会出错习题 18、19、20 分别展示了三个层次的检错纠错机制。奇偶校验最简单。32 位数据 8F 3C AB 96H 分成四个字节每个字节配一个奇校验位要求每 9 位里 1 的个数为奇数。8F 展开是 1000 1111有 5 个 1校验位 03C 是 0011 1100有 4 个 1校验位 1AB 是 1010 1011有 5 个 1校验位 096 是 1001 0110有 4 个 1校验位 1。接收端收到的数据变成 87 3C AB 96H校验位是 0101。重新计算前四个字节的奇校验87 只有 4 个 1期望校验位是 1收到的是 0异或结果为 1定位到第一个字节出错。SEC 海明码更进一步能指出具体哪一位错。16 位数据的 SEC 码用 5 个校验位放在 1、2、4、8、16 位其余位置放数据。每个校验位覆盖一组特定位置的数据位出错时重新计算校验位和发送方的校验位异或得到故障字故障字的二进制值正好是出错位的编号。习题 19 里数据错在第 5 位 M5故障字是 01001即 9对应码字第 9 位——正好是 M5 的位置。这个设计的巧妙之处在于校验位位置选 2 的幂让每个出错位都产生一个唯一的故障字。CRC 校验则是多项式的世界。习题 20 的数据是 100011生成多项式 G(x)x^31即二进制 1001。发送方在数据后补 3 个 0用模 2 除法去除以 1001余数是 111CRC 码就是 100011 111。接收方收到 100010 111同样做模 2 除法余数 001非 0判定出错。这里的除法不是算术除法是按位异或相当于判断接收到的多项式是否能被生成多项式整除。不能整除就说明有错这正是 CRC 能被广泛用于链路层的原因。5. 常见问题排查计组题里最常翻车的五个现场5.1 现象CPI 直接相比差多少就算快多少习题 6 后面有个思考题程序 P 在 M1 上 CPI 是 2.4M2 上 CPI 是 3.8有人说 M1 比 M2 快 (3.8-2.4)/3.8 36.8%。原因CPI 只是执行时间的一个因子这台题目里 M1 主频 1GHz、M2 主频 1.5GHz。完整的执行时间是 CPI × 指令条数 / 主频。假设指令条数为 NM1 是 2.4N nsM2 是 3.8N/1.5 2.53N nsM1 只快约 5%。CPI 直接比错在丢掉了时钟周期这一个维度。解决永远把执行时间公式三项写齐再算任何性能结论都要落到时间上。5.2 现象指令条数少程序一定更快习题 9 的 S1 有 10 条指令S2 只有 8 条S2 反而慢 14ns。原因S2 的 CPI 是 3.25是 S1 的 1.7 倍多出的 D 类指令全是高 CPI 指令。解决比较两个程序或两个编译版本要看 “指令数、CPI、时钟周期” 的乘积任何单一指标都不能直接证明快慢。5.3 现象十进制小数转二进制算几位就停习题 10 里1.7 转成二进制是无限循环小数。如果手算时取 0.1101 就收手后续的阶码、尾数全是错的。参考答案用 0 舍 1 入得到 0.011011×4^1阶码是 17尾数 0.011011。-0.12 转出来是 -0.000111101B要左移到 0.011111×4^-1 再取补码。原因浮点格式尾数位有限十进制小数在二进制里可能无限循环必须按格式位数做舍入。解决先确定格式有多少位尾数再决定保留几位最后一步做 0 舍 1 入不要提前截断。5.4 现象unsigned 的边界值当普通整数算习题 9 的 len0 时len-1 是 0xFFFFFFFF 而不是 -1循环不退出。习题 8 里 -1 0U 的结果是 0因为 -1 转成 unsigned 后是 4294967295。原因C 语言的隐式类型转换把有符号转成无符号负数变成大正数。解决涉及 unsigned 的循环条件不用 len-1 这种写法直接 i len混合比较时先想清楚两边会被转换成什么类型。5.5 现象除法优化偏置量想当然习题 5 的 y/4 优化有人觉得负数加 2 就行。加 2 对 y-4 恰好成立但对 y-3 就错了-32-1算术右移两位是 -1而 -3/4 向零取整是 0。原因偏置量必须是 2^k - 1目的是让 floor((ybias)/2^k) 等于 trunc(y/2^k)。解决不要背特例直接从向下取整和向零取整的关系推k 位右移配 2^k - 1 的偏置。6. 设计取舍每个聪明的设计都在“浪费”位宽这套习题答案看下来会发现一个规律计算机里每个关键设计都在用某种“浪费”换另一种收益。补码浪费了直观性换来的是加减法统一CPU 里少一套减法器移码浪费了纯二进制的大小顺序直觉换来浮点指数可以直接按无符号数比较IEEE 754 浪费了规格化数尾数最高位的一位空间但把这一位变成隐藏位多给了 24 位有效精度非规格化数浪费了指数编码空间换来的是下溢时数值能平滑逼近 0而不是从 2^-126 直接跳成 0。这些取舍在习题里体现得非常具体。习题 4 里 1.0 溢出而 -1.0 不溢出就是补码不对称性的代价习题 15 里最大规格化数和最小非规格化数相差那么多数量级就是浮点表示把范围撑大后精度分布不均的代价。做题的时候如果不看设计动机这些边界情况都像冷门考点但一旦理解了“每个位模式都有它的用途和代价”它们就成了验证设计合理性的检查点。从那以后我每次拿到一道计组题都强制自己先列出参与计算的公式和前提再代入数值最后才看结论。尤其是性能题和浮点题直觉几乎总是错的公式和位模式才是唯一可靠的工具。这份习题答案汇总适合放在手边边做边对照把每一道题的“为什么”推演一遍比直接背参考答案有用得多。希望帮到你。本文还有配套的精品资源点击获取