计算机组成原理
引言 考研大纲概述
全国硕士研究生计算机学科专业基础综合(408)考试,包含数据结构、计算机组成原理、操作系统和计算机网络4大专业基础课程,满分150分。其中,计算机组成原理45分,占总分的30%。
计算机组成原理的考察目标为:
-
掌握单处理器计算机系统中主要部件的工作原理、组成结构以及相互连接方式。
-
掌握指令集体系结构的基本知识和基本实现方法,对计算机硬件相关问题进行分析,并能够对相关部件进行设计。
-
理解计算机系统的整机概念,能够综合运用计算机组成的基本原理和基本方法,对高级编程语言(C语言)程序中的相关问题进行分析,具备软硬件协同分析和设计能力。
计算机组成原理,主要讲的就是计算机系统的“组成”,以及其中各部件的具体实现和连接的原理。
第一章 计算机系统概述
1.1 本章大纲要求与核心考点
1.1.1 大纲内容
(一) 计算机系统层次结构
-
计算机系统的基本组成
-
计算机硬件的基本结构
-
计算机软件和硬件的关系
-
计算机系统的工作原理
“存储程序"工作方式,高级语言程序与机器语言程序之间的转换,程序和指令的执行过程。
(二) 计算机性能指标
吞吐量、响应时间;
CPU时钟周期、主频、CPI、CPU执行时间;
MIPS、MFLOPS 、GFLOPS、TFLOPS、PFLOPS、EFLOPS、ZFLOPS。
1.1.2 核心考点
本章是计算机组成原理的概述,一般会考察相关概念,以选择题为主,也可能结合后续章节以综合题的形式来考察性能的分析。掌握本章的概念,是整个组成原理课程的基础。 计算机性能指标在历年真题中出现的频次较高,需要重点掌握。
1.1.3 真题分布
考点 | 考查次数 | |
---|---|---|
单项选择题 | 综合应用题 | |
计算机系统层次结构 | 6 | 0 |
计算机性能指标 | 9 | 2 |
1.2 计算机系统简介
1.2.1 计算机系统的概念和分类
计算机系统由“硬件”和“软件”两大部分组成。
-
所谓“硬件”即指计算机的实体部份,它由看得见摸得着的各种电子元器件、各类光、电、机设备的实物组成,如主机、外设等等。
-
所谓“软件”,它是看不见摸不着的,由人们事先编制成具有各类特殊功能的信息组成。通常把这些信息, 诸如各类程序寄寓于各类媒体中, 如RAM、ROM、磁带、磁盘、光盘、甚至纸带等。它们通常被作为计算机的主存或辅存的内容。
计算机的软件通常又可以分为两大类:系统软件和应用软件。
-
系统软件又称为系统程序,主要用来管理整个计算机系统,监视服务,使系统资源得到合理调度,确保高效运行。它包括:标准程序库、语言处理程序(如将汇编语言翻译成机器语言的汇编程序;将高级语言翻译成机器语言的编译程序)、操作系统(如批处理系统、分时系统、实时系统)、服务性程序(如诊断程序、调试程序、连接程序等)、数据库管理系统、网络软件等等。
-
应用软件又称为应用程序,它是用户根据任务需要所编制的各种程序。如科学计算程序,数据处理程序,过程控制程序,事务管理程序等等。
1.2.2 计算机的发展
(一) 硬件的发展
1943 年,第二次世界大战进入后期,因战争的需要,美国国防部主导建造了第一台计算机ENIAC(Electronic Numerical Integrator And Computer,ENIAC),它的全称是"用电子管组成的电子数字积分机和计算机”。
从此以后,计算机的发展经历了电子管、晶体管、集成电路的世代发展,体积越来越小、性能越来越强,并从军事领域迅速扩展应用到生活生产的各个行业,成为了现代信息社会不可或缺的基础设备。
硬件技术对计算机更新换代的影响
代 | 时间 | 硬件技术 | 速度(次/秒) |
---|---|---|---|
一 | 1946~1957 | 电子管 | 40, 000 |
二 | 1958~1964 | 晶体管 | 200, 000 |
三 | 1965~1971 | 中、小规模集成电路 | 1, 000, 000 |
四 | 1972~1977 | 大规模集成电路 | 10, 000, 000 |
五 | 1978~现在 | 超大规模集成电路 | 100, 000, 000 |
摩尔定律
集成电路出现之后,芯片集成度不断提高,从在一个芯片上集成成百上千个晶体管的中、小规模集成电路,逐渐发展到能集成成千上万个晶体管的大规模集成电路(LSI)和能容纳百万个以上晶体管的超大规模集成电路(VLSI)。
微芯片集成晶体管的数目增长非常迅速,其规律被总结为“微芯片上集成的晶体管数目每3年翻两番”,这就是所谓的“摩尔定律”。
摩尔定律的另一个常见表述是:每平方英寸电路板上的晶体管数量,每18个月翻一倍。
(二) 软件的发展
(1)编程语言的发展
软件的编写离不开编程语言。编程语言的发展经历了机器语言、汇编语言和高级语言三个阶段。我们现在使用的编程语言一般都是高级语言。
(2)操作系统的发展
随着计算机的发展及应用范围的扩大,逐渐形成了软件系统。而其中最重要的一类软件,是为了提高计算机性能和资源利用率而设计的,这就是“操作系统”。
操作系统的发展经历了批处理系统、分时系统、实时系统、PC操作系统、网络操作系统、分布式操作系统等多个阶段。目前我们的个人电脑使用的操作系统主要有:Windows、Mac OS、Linux。
1.3 计算机系统的层次结构
1.3.1 计算机系统的基本组成
(一) 计算机硬件
-
冯·诺依曼计算机
冯·诺依曼在研究EDVAC计算机时提出了 “存储程序”的概念,“存储程序”的思想奠定了现代计算机的基本结构,以此概念为基础的各类计算机通称为冯•诺依曼计算机,其特点如下:
-
采用“存储程序”的工作方式。
-
计算机硬件系统由运算器、存储器、控制器、输入设备和输出设备5大部件组成。
-
指令和数据以同等地位存储在存储器中,形式上没有区别,但计算机应能区分它们。
-
指令和数据均用二进制代码表示。
-
指令由操作码和地址码组成,操作码指出操作的类型,地址码指出操作数的地址。
-
计算机的功能部件
-
存储器:分为主存和辅存,中央处理器可以直接访问的程序和数据存放在主存中。
-
运算器:完成对信息或数据的处理和运算,如算术和逻辑运算。
-
控制器:完成对计算机各部件协同运行的指挥控制,即保证指令按预定的次序执行,保障每一条指令按规定的执行步骤正确执行,还要处理各项紧急事件。
-
输入设备:用来输入原始数据和程序,如键盘、鼠标。
-
输岀设备:用来输出计算机的处理结果,如显示器和打印机。
一般将运算器和控制器集成到同一个芯片上,称为中央处理器(CPU)。CPU和主存储器共同构成主机,而除主机外的其他硬件装置(外存、I/O设备等)统称为外部设备,简称外设。
(二) 计算机软件
-
软件的分类
软件按其功能分类,可分为系统软件和应用软件。
-
三个级别的计算机语言
(1) 机器语言
机器语言由二进制编码组成,它是计算机唯一可以直接识别和执行的语言。
(2) 汇编语言
汇编语言是用英文单词或其缩写代替二进制的指令代码,更容易为人们记忆和理解。汇编语言程序必须经过汇编操作,转换为机器语言后,才能在计算机硬件上执行。
(3) 高级语言
高级语言(如C、C++、Java等)程序需要先经过编译程序编译成汇编语言程序,再经过汇编操作 成为机器语言程序。高级语言程序也可直接通过解释的方式“翻译”成机器语言程序。
由于计算机无法直接理解和执行高级语言程序,因此需要将高级语言程序转换为机器语言程序,通常把进行这种转换的软件系统称为翻译程序。翻译程序有以下三类:
-
汇编程序(汇编器):将汇编语言程序翻译成机器语言程序。
-
解释程序(解释器):将高级语言源程序中的语句按执行顺序逐条翻译成机器指令并立即执行。
-
编译程序(编译器):将高级语言源程序翻译成汇编语言程序或机器语言程序。
1.3.2 计算机硬件的基本结构
典型的冯·诺依曼计算机是以运算器为中心的,如下图所示。其中,输入、输出设备与存储器之间的数据传送都需通过运算器。图中实线为数据线,虚线为控制线和反馈线。
现代的计算机已转化为以存储器为中心,如下图所示。图中实线为控制线,虚线为反馈线,双线为数据线。
图中各部件的功能是:
-
运算器用来完成算术运算和逻辑运算,并将运算的中间结果暂存在运算器内;
-
存储器用来存放数据和程序;
-
控制器用来控制、指挥程序和数据的输入、运行以及处理运算结果;
-
输入设备用来将人们熟悉的信息形式转换为机器能识别的信息形式,常见的有键盘、鼠标等。
-
输出设备可将机器运算结果转换为人们熟悉的信息形式,如打印机输出、显示器输出等。
由于运算器和控制器在逻辑关系和电路结构上联系十分紧密,尤其在大规模集成电路制作工艺出现后,这两大部件往往制作在同一芯片上,因此,通常将它们合起来统称为中央处理器(Central Processing Unit) , 简称CPU。把输入设备与输出设备简称为I/O设备(Input/Output equipment) 。
这样, 现代计算机可认为由三大部分组成:CPU、IO设备及主存储器(Main Memory,MM) 。CPU与主存储器合起来又可称为主机, I/O设备叫作外部设备。
-
主存储器是存储器子系统中的一类,用来存放程序和数据, 它可以直接与CPU交换信息。另一类叫辅助存储器, 简称辅存, 又叫外存。
-
ALU(Arithmetic Logic Unit) 算术逻辑运算单元,用来完成算术逻辑运算。
-
CU(Control Unit) 控制单元, 用来解释存储器中的指令, 并发出各种操作命令来执行指令。
ALU和CU是CPU的核心部件。I/O设备也受CU控制,用来完成相应的输入、输出操作。可见,计算机有条不紊地自动工作,都是在控制器统一指挥下完成的。
1.3.3 计算机软件和硬件的关系
硬件实现的往往是最基本的算术和逻辑运算功能,而其他功能大多通过软件的扩充得以实现。对某 一功能来说,既可以由硬件实现,也可以由软件实现。从用户的角度来看,它们在功能上是等价的。这 一等价性被称为软、硬件逻辑功能的等价性。
由于“软件”的发展,它不仅可以充分发挥计算机的“硬件”功能,提高计算机的工作效率,而且已经发展到能局部模拟人类的思维活动,因此在整个计算机系统内,“软件”的地位和作用已经成为评价计算机系统性能好坏的重要标志。当然,“软件”性能的发挥,也必须依托“硬件”的支撑。因此,概括而言,计算机性能的好坏,取决于 “软”、 “硬”件功能的总和。
1.3.4 计算机系统的多级层次结构
从用户的角度看,人们在操作系统提供的运行环境下,首先用高级语言编写程序(称为源程序),然后将源程序翻译成汇编语言程序,再将其翻译成机器能识别的机器语言程序(称为目标程序),最后用微程序解释每条机器指令。这样,就构成一个常见的计算机系统的5级层次结构,如下图所示:
从计算机系统的5级层次结构来看,可以将硬件研究的对象归结为微程序机器 M0 与传统机器 M1,也就是实际机器。而软件研究的对象主要是操作系统及其以上的各级虚拟机器。通常将除硬件系统外的其余层级称为虚拟机器,包括操作系统机器 M2、汇编语言机器 M3 和高级语言机器 M4。简单来说,虚拟机器就是由软件实现的机器。
相邻层级之间的关系,下层是上层的基础,上层是下层的扩展。随着超大规模集成电路技术的不断发展,部分软件功能可以由硬件来实现,所以软/硬件交界面的划分也不是绝对的。
1.3.5 计算机系统的工作原理
1. 从源程序得到可执行程序
用高级语言编写好一段程序之后,需要经过一系列“翻译“过程,才能得到计算机能够执行的机器代码。比如,我们用C语言写了一个简单的 hello world 程序,源程序文件命名为 hello.c,用GCC编译器可以将它翻译成一个可执行目标程序 hello。具体的过程可以分为4个阶段,如下图所示:
(1)预处理阶段:预处理器(cpp)对源程序中以 ”#“ 开头的命令进行处理,输出结果是一个以 ”.i“ 为扩展名的文件 hello.i。例如 ”#include“ 就会将后面的头文件内容插入程序文件中。
(2)编译阶段:编译器(ccl)对预处理后的源程序进行编译,生成一个汇编语言源程序 hello.s。汇编语言源程序中的每条语句,都用文本格式描述了一条机器语言指令。
(3)汇编阶段:汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一个”可重定位目标程序“ hello.o,它是一个二进制文件,用文本编辑器打开会显示乱码。
(4)链接阶段:链接器(ld)将多个可重定位目标程序和标准库函数合并成一个可执行目标程序。上面的例子中,链接器将 hello.o 和标准库函数 printf 所在的可重定位目标模块 printf.o 合并,生成可执行程序 hello。最终生成的可执行程序被保存在磁盘上。
2. 存储程序的基本思想
“存储程序”的基本思想,就是将程序和数据一样,存放在主存中;运行时通过地址访问到程序的内容,解析出对应的指令进行执行。
-
程序执行前,先将第一条指令的地址存放在程序计数器(PC)中;
-
将PC的内容作为地址访问主存,取出指令;
-
在每条指令执行过程中,都需要计算下一条将执行指令的地址,并送至PC。如果当前指令是顺序执行的,则下一条指令地址是PC的内容加上当前指令的长度;如果是跳转指令,则下一条指令的地址是指定的目标地址;
-
当前指令执行完毕后,再根据PC的值作为地址访问主存,取出的是下一条将要执行的指令。
这样,计算机就可以周而复始地自动执行程序中的每一条指令了。
3. 计算机硬件组成的细化说明
为了更清楚地了解计算机的工作过程,我们需要将计算机的组成部件进一步细化,如下图所示。
除了之前已经列出的核心部件 ALU、CU 以及主存的存储器M,CPU和主存储器中还必须配置一些寄存器(Register),用来存放特定的信息。下面我们分别进行简单介绍。
(1)主存储器
主存储器(简称主存或内存)包括了存储体M、各种逻辑部件以及控制电路等。存储体由许多存储单元组成,每个存储单元又包含若干个存储元件;每个存储元件能存放一位二进制代码(0或者1)。这样,每个存储单元可以存储一串二进制代码,这就被称为一个”存储字“;存储字的二进制位数称为”存储字长“。
主存中的每个存储单元有一个唯一的编号,叫做存储单元的”地址“(Address)。主存的工作方式就是按照存储单元的地址,来实现对存储字各位的存(写入)取(读出)。这种存取方式叫做”按地址访问存储器“。
为了实现按地址访问的方式,主存中还必须配置两个寄存器:MAR 和 MDR。
-
MAR(Memory Address Register,存储器地址寄存器):用来存放想要访问的存储单元的地址,它的位数决定了能访问的存储单元的最大个数。
-
MDR(Memory Data Register,存储器数据寄存器):用来存放从存储体单元中取出,或者准备向存储体单元存入的数据,它的位数和存储字相等。
当然,如果想要完整地实现一个存取数据的操作,还需要 CPU 给主存加上各种控制信号,比如读命令、写命令以及地址译码驱动信号等。随着硬件技术的发展,主存现在都是统一制作的大规模集成电路芯片,所以一般都将MAR 和 MDR 集成在 CPU 芯片中。
(2)运算器
运算器包括了一个算术逻辑单元(ALU)和最少三个寄存器。
-
ACC: Accumulator,累加器;
-
MQ: Multiplier-Quotient Register,乘商寄存器;
-
X: 操作数寄存器
这三个寄存器在完成不同的算术运算时,所存放的操作数也各不相同。具体的情况如下表所示:
加法 | 减法 | 乘法 | 除法 | |
---|---|---|---|---|
ACC | 被加数及和 | 被减数及差 | 乘积高位 | 被除数及余数 |
MQ | —— | —— | 乘数及乘积低位 | 商 |
X | 加数 | 减数 | 被乘数 | 除数 |
不同机器的运算器结构也有所不同,有的机器用 MDR 取代 X 寄存器。
(3)控制器
控制器由控制单元(CU)和程序计数器(PC)、指令寄存器(IR)组成。
PC: Program Counter,程序计数器,用来存放当前将要执行指令的地址。它与主存的 MAR 之间之间有一条直接通路,且具有自动加1的功能,也就是可以自动形成下一条指令的地址。
IR: Instruction Register,指令寄存器,用来存放当前的指令。IR 的内容来自主存的 MDR,包含了操作码和地址码。IR 中的操作码 OP(IR)会送至 CU,可以记作 OP(IR) → CU,用来分析指令;而地址码 Ad(IR)作为操作数的地址送至存储器的 MAR,可以记作 Ad(IR) → MAR。
CU 是控制器的核心组件,用来分析当前指令所需完成的操作,并发出各种微操作命令序列,从而控制所有被控对象。控制器是计算机的神经中枢,由它指挥各部件自动协调地工作;完成一条指令操作,需要取指、分析和执行3个阶段。
(4)I/O
I/O 系统包括各种 I/O 设备及其相应的接口。每一种 I/O 设备都由 I/O 接口与主机联系,它接收 CU 发出的各种控制命令,并完成相应的操作。
4. 计算机硬件的工作过程
总结一下,当计算机接收到机器语言程序后,硬件的工作过程分为以下几步:
-
把程序和数据装入主存储器;
-
从程序的起始地址运行程序;
-
按照程序的首地址从存储器中取出第一条指令,经过译码等步骤控制计算机各功能部件协同运行,完成这条指令的功能,并计算下一条指令的地址;
-
用新得到的指令地址继续读出第二条指令并执行,直到程序结束为止。每条指令都是在取指、译码和执行的循环过程中完成的。
我们现在以从主存中取数据的指令为例,详细分析一下它的执行过程:
(1)取指令: PC → MAR → M → MDR → IR
根据 PC 取指令到 IR。将 PC 的内容送至 MAR,将 MAR 的内容送至地址线,同时控制器将读信号送至读/写信号线,从主存指定存储单元读出指令,并通过数据线送至 MDR,再传送至 IR。
(2)分析指令: OP(IR) → CU
指令译码并送出控制信号。控制器根据 IR 中指令的操作码,生成相应的控制信号,送到不同的执行部件。这里 IR 中是取数指令,所以读控制信号被送到总线的控制线上。
(3)执行指令:Ad(IR) → MAR → M → MDR → ACC
取数操作。将 IR 中指令的地址码送至 MAR,将 MAR 的内容送至地址线,同时控制器将读信号送至读/写信号线,从主存指定存储单元读出操作数,并通过数据线送至 MDR,再传送到 ACC 中。
(4)每取完一条指令,还必须计算下一条指令的地址,为取下一条指令做准备:(PC)+ 1 → PC
1.4 计算机性能指标
衡量一台计算机的性能是由多项技术指标综合确定的,既包含硬件的各类性能,又包括软件的各种功能,这里主要讨论硬件的技术指标。
1.4.1 主要性能指标
-
机器字长
机器字长,是指 CPU 一次能处理数据的位数,也就是 CPU 内部用于整数运算的数据通路的宽度。字长通常就等于 CPU 的通用寄存器宽度,也就是 CPU 内用于整数运算的运算器位数,它反映了计算机处理信息的能力。我们平常所说”一台16位或32位的机器“,这里的16、32就是指字长。
字长越长,数的表示范围也越大,精度也越高。机器的字长也会影响机器的运算速度。倘若CPU字长较短, 又要运算位数较多的数据, 那么需要经过两次或多次的运算才能完成,这样势必影响整机的运行速度。当然,机器字长对硬件的造价也有较大的影响。它将直接影响加法器(或ALU) 、数据总线以及存储字长的位数。
-
指令字长:一条指令中包含的二进制代码的位数。
-
存储字长:一个存储单元中存储的二进制代码的长度。
指令字长和存储字长,都必须是字节(Byte)的整数倍。指令字长一般取存储字长的整数倍:如果指令字长等于存储字长的2倍,那么取一条指令就需要2个机器周期;如果指令字长等于存储字长,那么取指周期就等于机器周期。
-
数据通路带宽
数据通路带宽,是指数据总线一次所能并行传送信息的位数,它关系到数据的传送能力。这里所说的数据通路带宽是指外部数据总线的宽度,它与 CPU 内部的数据总线宽度(机器字长)可能不同。
-
存储容量
存储器的容量,包括主存容量和辅存容量。我们一般主要关心主存容量。
主存容量是指主存中所能存储信息(二进制代码)的最大容量,通常以字节数来衡量。
存储容量 = 存储单元个数 × 存储字长
在主存储器中,MAR 的位数反映了存储单元的个数, MDR 的位数则反映了存储字的长度。例如, MAR 为16位, 表示 216 = 65536, 也就是说对应的存储体内有65536个存储单元(一般称为64K内存, 1K=1024);而如果 MDR 为32位, 那么主存的存储容量为 216 × 32 = 221 = 2M 位(1M=220)。
现代计算机中常以字节的个数来描述容量的大小,一个字节(Byte)被定义位8位二进制代码。所以上述存储容量是 2M 位(bit),也可用 218 字节(Byte,简写为 B)表示,记作 218 B 或 256KB。
同理,辅存容量也可用字节数来表示,例如,某机辅存(如硬盘)容量为 128 GB(1G = 1KM = 230 )。
-
运算速度
计算机的运算速度与许多因素有关,如机器的主频、CPU 的结构、执行什么样的操作、主存本身的速度(主存速度快,取指、取数就快)等等都有关。
1.5.2 专业术语解释
吞吐量:系统在单位时间内处理请求的数量,主要取决于主存的存取周期。
响应时间:从用户向计算机发送一个请求,到系统对该请求做出响应并获得所需结果的时间。通常包括 CPU 时间(计算机执行程序的时间)和等待时间(用于磁盘访问、存储器访问、I/O操作等的时间)。
主频(CPU 时钟频率):机器内部主时钟的频率,它是衡量机器速度的重要参数。对统一型号的计算机,主频越高,完成指令的一个步骤所用的时间越短,执行指令的速度越快。通常以赫兹(Hz)为单位。
CPU 时钟周期:节拍脉冲的宽度或周期,也就是主频的倒数,它是 CPU 中最小的时间单位。
CPU 时钟周期 = 1 / 主频
CPI:Clock cycle Per Instruction,执行一条指令所需的时钟周期数。对一个程序或一台机器来说,CPI 指的是该程序或该机器指令集中所有指令执行所需要的平均时钟周期数。
CPU 执行时间:运行一个程序所花费的时间。
CPU 执行时间 = (指令数 × CPI)/ 主频
对于同一个程序,CPU 的执行时间就代表了 CPU 的性能,它主要取决于三个要素:主频、CPI 和 指令数。这三者是相互制约的。不同的机器可以有不同的指令集,更改指令集可以让程序的指令数更少,但 CPI 可能就会增大;同时可能引起 CPU 结构的调整,从而造成主频的降低。
现在机器的运算速度,普遍采用单位时间内执行指令的平均条数来衡量,并用 MIPS(Million Instruction Per Second)作为计量单位, 即每秒执行百万条指令。比如,某机每秒能执行200万条指令, 则记作2 MIPS。
MIPS:Million Instructions Per Second,每秒执行百万条指令的数目。
MIPS = 主频 /(CPI × 106)
FLOPS:FLoating-point Operations Per Second,每秒执行浮点运算的次数。
MFLOPS:百万次浮点运算每秒。 MFLOPS = 浮点操作次数 /(执行时间 * 106)
GFLOPS:十亿次浮点运算每秒。 GFLOPS = 浮点操作次数 /(执行时间 * 109)
TFLOPS:万亿次浮点运算每秒。 TFLOPS = 浮点操作次数 /(执行时间 * 1012)
PFLOPS:千万亿次浮点运算每秒。 PFLOPS = 浮点操作次数 /(执行时间 * 1015)
EFLOPS:百京次浮点运算每秒。 EFLOPS = 浮点操作次数 /(执行时间 * 1018)
ZFLOPS:十万京次浮点运算每秒。 ZFLOPS = 浮点操作次数 /(执行时间 * 1021)
需要注意,在计算机中,描述存储容量、文件大小时,K、M、G、T 等数量单位通常用2的幂次表示,比如 1 KB = 210 B;而在描述速率、频率等概念时,通常用10的幂次表示,比如 1 kb/s = 103 b/s。
1.5 章节练习
一、单项选择题
-
【2009真题】冯 · 诺依曼计算机中指令和数据均以二进制形式存放在存储器中,CPU 区分它们的依据是 ( )。
A. 指令操作码的译码结果 B. 指令和数据的寻址方式
C. 指令周期的不同阶段 D. 指令和数据所在的数据单元
答案: C
-
【2015真题】计算机硬件能够直接执行的是 ( )。
I. 机器语言程序 II. 汇编语言程序 III. 硬件描述语言程序
A. 仅 I B. 仅 I、II C. 仅 I、III D. I、II、III
答案: A
-
【2016真题】将高级语言源程序转换为机器级目标代码文件的程序是 ( )。
A. 汇编程序 B. 链接程序 C. 编译程序 D. 解释程序
答案: C
-
【2019真题】下列关于冯 · 诺依曼结构计算机基本思想的叙述中,错误的是 ( )。
A. 程序的功能都通过中央处理器执行指令实现 B. 指令和数据都用二进制表示,形式上无差别
C. 指令按地址访问,数据都在指令中直接给出 D. 程序执行前,指令和数据需预先存放在存储器中
答案: C
-
【2020真题】下列给出的部件中,其位数(宽度)一定与机器字长相同的是 ( )。
I. ALU II. 指令寄存器 III. 通用寄存器 IV. 浮点寄存器
A. 仅 I、II B. 仅 I、III C. 仅 II、III D. 仅 II、III、IV
答案: B
-
【2010真题】下列选项中,能缩短程序执行时间的是( )。
I. 提高 CPU 时钟频率 II. 优化数据通路结构 III. 对程序进行编译优化
A. 仅 I 和 II B. 仅 I 和 III C. 仅 II 和 III D. I、II、III
答案: D
-
【2011真题】下列选项中,描述浮点数操作速度的是( )。
A. MIPS B. CPI C. IPC D. MFLOPS
答案: D
-
【2012真题】假定基准程序 A 在某计算机上的运行时间为 100s,其中 90s 为 CPU 时间,其余为 I/O 时间。若 CPU 速度提高 50%,I/O 速度不变,则运行基准程序 A 所耗费的时间是 ( )。
A. 55s B. 60s C. 65s D. 70s
答案: D
-
【2013真题】某计算机的主频为 1.2 GHz,其指令分为4类,它们在基准程序中所占比例及CPI如下表所示。
指令类型 | 所占比例 | CPI |
---|---|---|
A | 50% | 2 |
B | 20% | 3 |
C | 10% | 4 |
D | 20% | 5 |
该机的 MIPS 是 ( )。
A. 100 B. 200 C. 400 D. 600
答案: C
-
【2014真题】程序 P 在机器 M 上的执行时间是 20s,编译优化后,P 执行的指令数减少到原来的70%,而 CPI 增加到原来的1.2倍,则 P 在 M 上的执行时间是 ( )。
A. 8.4s B. 11.7s C. 14s D. 16.8s
答案: D
-
【2017真题】假定计算机 M1 和 M2 具有相同的指令集体系结构(ISA),主频分别为 1.5GHz 和 1.2 GHz。在 M1 和 M2 上运行某基准程序 P,平均 CPI 分别为 2 和 1,则程序 P 在 M1 和 M2 上运行时间的比值是 ( )。
A. 0.4 B. 0.625 C. 1.6 D. 2.5
答案: C
二、综合应用题
-
说明机器字长、指令字长、存储字长的区别和联系。
答案:
机器字长:计算机能直接处理的二进制数据的位数,机器字长一般等于内部寄存器的大小,它决定了计算机的运算精度。
指令字长:一个指令字中包含二进制代码的位数。
存储字长:一个存储单元存储二进制代码的长度。
它们都必须是字节的整数倍。指令字长一般取存储字长的整数倍,如果指令字长等 于存储字长的2倍,就需要2次访存来取出一条指令,因此,取指周期为机器周期的2倍;如果指令字长等于存储字长,则取指周期等于机器周期。早期的计算机存储字长一般和机器的指令字长与数据字长相等,故访问一次主存便可以取出一条指令或一个数据。随着计算机的发展,指令字长可变,数据字长也可变,但它们都必须是字节的整数倍。
-
用一台 40MHz 的处理器执行基准程序,它所包含的混合指令数和响应所需的时钟周期见下表。求有效的 CPI、MIPS 和程序的执行时间(程序的指令条数为 I)。
指令类型 CPI 指令混合比 算术和逻辑 1 60% 转移 4 12% 高速缓存命中的访存 2 18% 高速缓存失效的访存 8 10%
答案:
CPI 是执行一条指令所需的平均时钟周期数。本程序中包含4种指令,根据它们不同的占比,CPI 就是这4种指令的数学期望:
CPI = 1 × 60% + 4 × 12% + 2 × 18% + 8 × 10% = 0.6 + 0.48 + 0.36 +0.8 = 2.24
MIPS 是每秒执行的百万条指令数。已知时钟频率为 40MHz,也就是每秒有 40M 个时钟周期,所以:
MIPS = 40 × 106 /(2.24 × 106 )≈ 17.9
程序的执行时间 T = 平均每条指令执行时间 × 指令条数,而平均每条指令执行的时间,就是 CPI × 时钟周期:
T = CPI × 时钟周期 × 指令条数 = 2.24 ×(1 / 40MHz)× I = 5.6 × 10-8 × I 秒
第二章 数据的表示和运算
2.1 本章大纲要求与核心考点
2.1.1 大纲内容
(一)数制与编码
-
进位计数制及其数据之间的相互转换
-
定点数的表示和运算
(二)运算方法和运算电路
-
基本运算部件:加法器、算数逻辑部件(ALU)
-
加/减运算:补码加/减运算器,标志位的生成
-
乘/除运算:乘/除运算的基本原理,乘法电路和除法电路的基本结构
(三)整数的表示和运算
-
无符号整数的表示和运算
-
有符号整数的表示和运算
(四)浮点数的表示和运算
-
浮点数的表示:IEEE 754标准
-
浮点数的加/减运算
2.1.2 核心考点
本章内容是考研考察的一个重点和难点,往往会有综合应用题出现。
需要重点掌握的内容包括:
-
真值、机器数,定点数的表示及原理
-
C 语言中的整型数据,有符号数与无符号数、不同字长整数之间的类型转换
-
ALU 的基本组成,标志位的产生,定点数的运算及相关电路,溢出概念与判断方法
-
IEEE 754标准浮点数的表示和特点,浮点数的加/减运算方法
-
C 语言中的浮点型数据,浮点型与整型、浮点型之间的类型转换,隐式类型转换
-
数据按边界对齐方式的存储,数据按大端和小端方式存储
2.1.3 真题分布
考点 | 考查次数 | |
---|---|---|
单项选择题 | 综合应用题 | |
定点数的表示与运算 | 10 | 8 |
IEEE 754标准浮点数,浮点数的运算 | 10 | 3 |
C语言中各种数据的转换 | 3 | 2 |
数据按边界对齐方式的存储,数据按大小端方式存储 | 4 | 0 |
2.2 数制与编码
计算机的应用领域极其广泛,但不论其应用在什么地方,信息在机器内部的形式都是一致的,采用的是二进制的表达,即均为0和1组成的各种编码。
2.2.1 进位计数制及其相互转换
(一)进位计数制
进位计数制简称“进制”,是人为定义的一种带进位的计数方法,可以用有限的数字符号表示所有的数。定义好的数字符号的个数,称为基数;当计数超出基数个数时,就需要向前进位。基数为n的进位计数制,就被称为“n进制”,特点是“逢n进一”。
我们日常生活中最常见的是十进制,使用0~9十个阿拉伯数字,逢十进一;而计算机系统底层的信息,使用的是二进制,也就是只有0和1两个数字,逢二进一。在计算机系统中,也经常使用八进制和十六进制来表示数据。下表是十进制数、二进制数、十六进制数对照表。
书写时,可在十六进制数后面加上“H”,如17DBH 或(17DB)16;八进制数后面加上“O”,如372O或(372)8;若在数的后面加上“B”,如10101100B,即表示此数为二进制数,或写成(10101100)2。
十进制数、二进制数、八进制数、十六进制数对照表
十进制数 | 二进制数 | 八进制数 | 十六进制数 | 十进制数 | 二进制数 | 八进制数 | 十六进制数 |
---|---|---|---|---|---|---|---|
0 | 00000 | 0 | 0 | 16 | 10000 | 20 | 10 |
1 | 00001 | 1 | 1 | 17 | 10001 | 21 | 11 |
2 | 00010 | 2 | 2 | 18 | 10010 | 22 | 12 |
3 | 00011 | 3 | 3 | 19 | 10011 | 23 | 13 |
4 | 00100 | 4 | 4 | 20 | 10100 | 24 | 14 |
5 | 00101 | 5 | 5 | 21 | 10101 | 25 | 15 |
6 | 00110 | 6 | 6 | 22 | 10110 | 26 | 16 |
7 | 00111 | 7 | 7 | 23 | 10111 | 27 | 17 |
8 | 01000 | 10 | 8 | 24 | 11000 | 30 | 18 |
9 | 01001 | 11 | 9 | 25 | 11001 | 31 | 19 |
10 | 01010 | 12 | A | 26 | 11010 | 32 | 1A |
11 | 01011 | 13 | B | 27 | 11011 | 33 | 1B |
12 | 01100 | 14 | C | 28 | 11100 | 34 | 1C |
13 | 01101 | 15 | D | 29 | 11101 | 35 | 1D |
14 | 01110 | 16 | E | 30 | 11110 | 36 | 1E |
15 | 01111 | 17 | F | 31 | 11111 | 37 | 1F |
计算机系统为什么要采用二进制?
-
使用有两个稳定状态的物理器件就可以表示二进制数的每一位,制造成本比较低。
-
二进制的1和0正好与逻辑值“真”和“假”对应,为计算机实现逻辑运算提供了便利。
-
二进制的编码和运算规则都很简单,通过逻辑门电路能方便地实现算术运算。
(二)不同进制数的相互转换
任意一个数 N,可以用 r 进制表示成下面的形式:
N =(dn-1dn-2 … d1d0.d-1d-2 … d-m)
= dn-1rn-1 + dn-2rn-2 + … + d1r1 + d0r0 + d-1r-1 + d-2r-2 + … + d-mr-m
= ∑ diri
其中,r 为基数;d 为系数,di 代表第 i 位上的数,可以是 0 ~ (r-1) 中的任意一个数字;ri 叫做第 i 位上的权值。n、m 分别代表 N 的整数部分和小数部分的位数。
(1)二进制和八进制、十六进制间的转换
二进制数数位较多,书写不方便,在计算机系统中一般需要进行“缩写”。由于 23=8, 24=16,从而3位二进制数就对应着一个8进制数、4位二进制数对应着一个16进制数;对于一个小数而言,以小数点为界,整数部分从小数点左侧第一位起向左数,小数部分从小数点右侧第一位起向右数,不够就补0。这样二进制数和八进制数、十六进制数就可以非常方便地互相转换了。
例如,将二进制数1110011101.0010111转换为八进制数为:
左侧补0 分界点 右侧补0
↓ ↓ ↓
001 110 011 101 . 001 011 100
1 6 3 5 . 1 3 4
所以 (1110011101.0010111)2 = (1635.134)8 ;
同样道理,转换为十六进制数为:
0011 1001 1101 . 0010 1110
3 9 D . 2 E
所以 (1110011101.0010111)2 = (39D.2E)16 ;
-
二进制转换为八进制:每数三位就转换成对应的八进制数,位数不够则补0。
-
二进制转换为十六进制:每数四位就转换成对应的十六进制数,位数不够则补0。
-
八进制转换为二进制:每位都转换成对应的3位二进制数。
-
十六进制转换为二进制:每位都转换成对应的4位二进制数。
(2)任意进制数转换为十进制数
任意进制数的各位数码与它的权值相乘,再把乘积相加,即得到相应的十进制数。这种转换方式称为 按权展开法。
例如,将二进制数 11011.101 转换为十进制数为:
(11011.101)2 = 1 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 1 × 20 + 1 × 2-1 + 0 × 2-2 + 1 × 2-3
= 27.625
另一种方法是“按基数重复相乘/除法”,需要分整数部分和小数部分分别转换。
整数部分从高到低,将每一位乘以基数值、再加上后一位,进行“重复相乘”:
(11011)2 = (((1 × 2 + 1) × 2 + 0 ) × 2 + 1) × 2 + 1 = 27
小数部分从低到高,将每一位除以基数值、再加上前一位,进行“重复相除”:
(0.101)2 = ((1 ÷ 2 + 0) ÷ 2 + 1 ) ÷ 2 + 0 = 0.625
(3)十进制数转换为二进制数
将十进制数转换为二进制数,一般采用 基数乘除法。整数部分和小数部分分别处理,最后将整数部 分与小数部分的转换结果拼接起来。
-
整数部分的转换规则:除2取余,最先取得的余数为数的最低位,最后取得的余数为数的最高位,商为0时结束。 (即除2取余,先余为低,后余为高)
-
小数部分的转换规则:乘2取整,最先取得的整数为数的最高位,最后取得的整数为数的最低位,乘积为0或精度满足要求时结束。(即乘2取整,先整为高,后整为低)
例如,将十进制数 123.6875 转换为二进制数。
整数部分:
除2得商 余数
2 |123 … 1 最低位
2 |61 … 1
2 |30 … 0
2 |15 … 1
2 |7 … 1
2 |3 … 1
2 |1 … 1 最高位
2 |0
所以 (123)10 = (1111011)2
小数部分:
乘积取小数 乘2得积 取整数部分
0.6875 × 2 = 1.375 1 最高位
0.375 × 2 = 0.75 0
0.75 × 2 = 1.5 1
0.5 × 2 = 1 1 最低位
所以 (0.6875)10 = (0.1011)2
综合整数和小数部分,得到 (123.6875)10 = (1111011.1011)2
另一种方法是“减权定位法”,利用记忆好的2的幂次的十进制表示,从原始数中依次减去所含最大的2的幂次,就可以快速得到对应的结果。例如,对于十进制数123:
十进制数 位权 转换后的结果
123 26 25 24 23 22 21 20
➖ 64 26 1
59
➖ 32 25 1
27
➖ 16 24 1
11
➖ 8 23 1
3
➖ 2 21 1
1
➖ 1 20 1
0
所以 (123)10 = (1111011)2
这种方法一般在转换很大的十进制数时比较方便。
2.2.2 真值和机器数
在计算机中,如果不加特别的定义,用二进制存储的数都是非负数,不需要加正负号,也就是“无符号数”。
对有符号数而言,符号的“正”、“负”机器本身是无法识别的;不过由于“正”、“负”恰好是两种截然不同的状态,我们可以用“0”表示“正”,用“1”表示“负”,这样符号也被数字化了,并且规定将它放在有效数字的前面,即组成了有符号数。
例如,一个有符号的小数:
+ 0.1011 在机器中表示为 0 1 0 1 1
- 0.1011 在机器中表示为 1 1 0 1 1
再比如,一个有符号的整数:
+ 1100 在机器中表示为 0 1 1 0 0
- 1100 在机器中表示为 1 1 1 0 0
把符号“数字化”的数称为机器数,而把带“+”或“-”符号的数称为真值。一旦符号数字化后,符号和数值就形成了一种新的编码。
-
真值:正、负号加某进制数绝对值的形式,即机器数所代表的实际值。
-
机器数:一个数值数据的机内编码,即符号和数值都数码化的数。常用的有原码和补码表示法等,这几种表示法都将数据的符号数字化,通常用“0”表示“正”,用“1”表示“负”。
在计算机中,小数点不用专门的器件表示,而是按约定的方式标出。根据小数点位置是否固定,可以分为两种方法表示小数点的存在,即定点表示和浮点表示。
另外需要考虑的问题是:在运算过程中,符号位能否和数值部分一起参加运算?如果参加运算,符号位又需作哪些处理?这些问题都与符号位和数值位所构成的编码有关。
在现代计算机中,通常用定点补码整数表示整数,用定点原码小数表示浮点数的尾数部分,用移码表示浮点数的阶码部分。
2.2.3 定点数及其编码表示
小数点固定在某一位置的数为定点数,有以下两种格式。
当小数点位于数符和第一数值位之间时,机器内的数为纯小数;当小数点位于数值位之后时,机器内的数为纯整数。采用定点数的机器称为定点机。数值部分的位数n决定了定点机中数的表示范围。
在定点机中,由于小数点的位置固定不变,故当机器处理的数不是纯小数或纯整数时,必须乘上一个比例因子,否则会产生“溢出”。
1. 无符号整数的表示
当一个编码的全部二进制位均为数值位时,相当于数的绝对值,该编码表示无符号整数。在字长相同的情况下,它能表示的最大数比带符号整数大。例如,8位无符号整数的表示范围为 0 ~ 28-1,也就是能表示的最大数为255;而8位带符号整数的最大数是127。通常,在全部是正数运算且不出现负值结果的情况下,使用无符号整数表示。例如,可用无符号整数进行地址运算,或用它来表示指针。
2. 带符号数的表示
最高位用来表示符号位,而不再表示数值位。
(1)定点整数
约定小数点在有效数值部分最低位之后。数据 x = x0x1x2…xn (其中 x0 为符号位,x1 ~ xn 是数值的有效部分,也称尾数),在计算机中的表示形式如图所示:
(2)定点小数
约定小数点在有效数值部分最高位之前。数据 x = x0.x1x2...xn(其中 x0 为符号位,x1~xn 是尾数,x1 是最高有效位),在计算中的表示形式如下图所示:
事实上,在计算机中,并没有小数点的表示,只是认为约定了小数点的位置:小数点在最右边的就是定点整数,在最左边的就是定点小数。它们原理相同,只是由于小数点位置不同而可以表示不同范围的数。我们这里重点只考虑定点整数就可以了。
3. 原码、补码、反码和移码
对于有符号的定点数,真实底层的机器数怎样表示,跟选择的编码方式有关。计算机中常用的编码方式有原码、补码、反码和移码。
-
原码表示法
用机器数的最高位表示数的符号,其余各位表示数的绝对值。纯小数的原码定义如下:
式子中 x 为真值,[ x ]原 表示原码机器数。
类似,纯整数的原码定义如下:
原码的性质:
-
由符号位与数的绝对值组成,符号位是0为正、1为负
-
简单直观,与真值的转换简单
-
0有 ±0 两个编码,即 [+0]原 = 00000 和 [-0]原 = 10000
-
原码加减运算规则比较复杂,乘除运算规则简单
-
补码表示法
纯整数的补码定义为:
这里 n 为整数的位数,真值 x 和补码机器数 [ x ]原 互为以 2n+1 为模的补数。如果字长为 n+1,那么补码的表示范围为 -2n ≤ x ≤ 2n - 1,比原码多表示了一个数 -2n。
补码的性质:
-
补码和其真值的关系:[x]补 = 符号位 × 2n+1 + x
-
0的编码唯一,因此整数补码比原码多1个数,表示 -2n
-
符号位参与补码加减运算,统一采用加法操作实现
-
将 [x]补 的符号位与数值位一起右移并保持原符号位的值不变,可实现除法功能
例如,当 x = + 1010 时(n = 4),
[x]补 = 0, 1010
而当 x = - 1010 时,
[x]补 = 2n+1 + x = 100000 - 1010 = 1, 0110
补码和真值的转换:
-
真值转为补码:对于正数, 与原码的转换方式一样;对于负数,符号位为1,其余各位由真值“取反加1”得到。
-
补码转为真值:若符号位为0,真值为正,跟原码的转换一样;若符号位为1,真值为负,其数值部分(绝对值)各位由补码“取反加1”得到。
变形补码是采用双符号位的补码表示法,其定义为
变形补码用于算术运算的ALU部件中,双符号位00表示正,11表示负,10和01表示溢出。
-
反码表示法
负数的补码可采用“数值位各位取反,末位加1”的方法得到,如果数值位各位取反而末位不加1,那么就是负数的反码表示。正数的反码定义和相应的补码(或原码)表示相同。
反码表示存在以下几个方面的不足:0的表示不唯一(即存在±0);表示范围比补码少一个最小负 数。反码在计算机中很少使用,通常用作数码变换的中间表示形式。
原码、补码、反码三种编码表示总结如下:
-
三种编码的符号位相同,正数的机器码相同。
-
原码和反码的表示在数轴上对称,二者都存在 ±0 两个零。
-
补码的表示在数轴上不对称,0的表示唯一,补码比原码和反码多表示一个数。
-
负数的反码、补码末位相差1。
-
原码很容易判断大小。而负数的补码和反码很难直接判断大小,可采用这条规则快速判断:对于一个负数,数值部分越大,它的绝对值就越小,所以真值就越大(更靠近0)。
-
移码表示法
移码是在真值 x 上加上偏置值 2n 构成的,相当于 x 在数轴上向正方向偏移了若干单位。
移码定义为:
移码的性质:
-
0的表示唯一, [+0]移 = 2n + 0 = [-0]移 = 2n - 0 = 100...0
-
符号位“1”表示正,“0”表示负,这与其他机器数正好相反。
-
一个真值的移码和补码仅差一个符号位,[x]补 的符号位取反即得 [x]移,反之亦然。
-
移码全0时,对应真值的最小值- 2n ;移码全1时,对应真值的最大值 2n -1。
-
保持了数据原有的大小顺序,移码大真值就大,便于进行比较操作。
-
移码常用来表示浮点数的阶码。它只能表示整数。
2.2.4 C 语言中的整型数据类型
-
C 语言中的整型数据简介
C 语言中的整型数据就是定点整数,一般用补码表示。根据位数的不同,可以分为 字符型(char)、短整型(short)、整型(int)、长整型(long)。
C 语言中的整型数据,可以分为 无符号整型 和 有符号整型 两种类型,在定义时只要加上 signed/unsigned 就可以明确指定了。
char 是整型数据中比较特殊的一种,其他如 short/int/long 等都默认是带符号整数,但 char 默认是无符号整数。无符号整数(unsigned short/int/long)的全部二进制位均为数值位,没有符号位,相当于数的绝对值。
signed/unsigned 整型数据都是按补码形式存储的,在不溢出条件下的加减运算也是相同的,只是 signed 型的最高位代表符号位,而在 unsigned 型中表示数值位,而这两者体现在输出上则分别是%d 和%u。
-
有符号数和无符号数的转换
C 语言允许在不同的数据类型之间做类型转换。C 语言的强制类型转换格式为“TYPE b = (TYPE) a”, 强制类型转换后,返回一个具有TYPE类型的数值,这种操作并不会改变操作数本身。
先看由 short 型转换到 unsigned short 型的情况。考虑如下代码片段:
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">short</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">4321</span>;
<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span>)<span style="color:#000000">x</span>;</span></span>
执行上述代码后,x = -4321, y = 61215,得到的 y 似乎与原来的 x 没有一点关系。不过将这两个数转化为二进制表示时,我们就会发现其中的规律。
通过本例可知:强制类型转换的结果是保持每位的值不变,仅改变了解释这些位的方式。有符号数转化为等长的无符号数时,符号位解释为数据的一部分,负数转化为无符号数时数值将发生变化。同理,无符号数转化为有符号数时,最高位解释为符号位,也可能发生数值的变化。
-
不同字长整数之间的转换
另一种常见的运算是在不同字长的整数之间进行数值转换。
先看长字长变量向短字长变量转换的情况。考虑如下代码片段:
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">int</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#116644">165537</span>, <span style="color:#000000">u</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">34991</span>; <span style="color:#aa5500">//int型占用4字节</span>
<span style="color:#008855">short</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> ( <span style="color:#008855">short</span> )<span style="color:#000000">x</span>, <span style="color:#000000">v</span> <span style="color:#981a1a">=</span> ( <span style="color:#008855">short</span> )<span style="color:#000000">u</span>; <span style="color:#aa5500">//short型占用2字节</span></span></span>
执行上述代码后,x= 165537, y=-31071, u =-34991, v = 30545。x、y、u、v 的十六进制表示分别 是0x000286a1 0x86a1 . 0xffff7751、0x7751。由本例可知:长字长整数向短字长整数转换时,系统把多余的高位部分直接截断,低位直接赋值,因此也是一种保持位值的处理方法。
最后来看短字长变量向长字长变量转换的情况。考虑如下代码片段:
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">short</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">4321</span>;
<span style="color:#008855">int</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">int</span>)<span style="color:#000000">x</span>;
<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span> <span style="color:#000000">u</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span>)<span style="color:#000000">x</span>;
<span style="color:#008855">unsigned</span> <span style="color:#008855">int</span> <span style="color:#000000">v</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">int</span>)<span style="color:#000000">u</span>;</span></span>
执行上述代码后,x = -4321, y = -4321, u = 61215, v = 61215。x、y、u、v 的十六进制表示分别是0xef1f. 0xffffef1f、0xef1f、0x0000ef1f。所以,短字长整数向长字长整数转换时,仅要使相应的位值相等,还要对高位部分进行扩展。如果原数字是无符号整数,则进行零扩展,扩展后的高位部分用 0填充。否则进行符号扩展,扩展后的高位部分用原数字符号位填充。其实两种方式扩展的高位部分都可理解为原数字的符号位。
从位值与数值的角度看,前3个例子的转换规则都是保证相应的位值相等,而短字长到长字长的转换可以理解为保证数值的相等。
2.3 运算方法和运算电路
2.3.1 基本运算部件
1. 运算器的基本组成
运算器由算术逻辑单元(ALU)、累加器(AC)、状态寄存器(PSW)、通用寄存器组等组成。
-
算术逻辑单元:完成加、减、乘、除四则运算,与、或、非、异或等逻辑运算。
-
累加器:暂存参加运算的操作数和结果的部件,为 ALU 执行运算提供一个工作区。
-
状态寄存器:也称作标志寄存器,用来记录运算结果的状态信息。
-
通用寄存器组:保存参加运算的操作数和运算结果。
2. 逻辑门电路和逻辑运算(复习)
用半导体元器件可以构建出基本的逻辑门电路(与、或、非),能够表示基本的逻辑运算。
通过对与门、或门、非门的组合,可以构建出更加复杂的逻辑电路,进行各种复杂的组合逻辑运算。
逻辑运算中的“与”类似于算术中的乘法,“或”类似于算术中的加法,两者组合在一起时,与运算的优先级要更高。逻辑运算满足以下的规则:
3. 全加器(复习)
全加器(Full Adder,FA),是用逻辑门电路实现两个二进制数相加并求出和的组合线路,这称为一位全加器。一位全加器可以处理低位进位,并输出本位加法进位。多个一位全加器进行级联可以得到多位全加器。
一位全加器的真值表如下所示,其中 A 为被加数,B 为加数,相邻低位传来的进位数为 Cin,输出本位和为S,向相邻高位输出的进位数为 Cout 。
根据真值表,很容易写出一位全加器的输出表达式:
所以,一位全加器可以利用两个异或门、两个与门和一个或门来实现:
4. 并行加法器
对于 n 位加法器,可以用 n 个全加器(实现两个本位数加上低位进位,生成一个本位和一个向高位的进位)串接起来实现逐位相加,位间进行串行传送,称为 串行进位加法器。
这样,一位全加器的输出表达式可以写为:
在串行进位链中,进位按串行方式传递,高位仅依赖低位进位,因此速度较慢。
为了提高加法器的速度,必须尽量避免进位之间的依赖。引入进位生成函数和进位传递函数,可以使各个进位并行产生,这种以并行进位方式实现的加法器称为 并行进位加法器。
在全加器的表达式中可以看到,进位信号 Ci 由两部分组成:
-
AiBi 与低位无关,可以称为“本地进位”,记作 di;
-
(Ai + Bi) Ci-1 与低位进位 Ci-1 有关,可以称为“传递进位”,系数 (Ai + Bi) 称作“传递系数”,记作 ti。
这样进位信号就可以简写为:
以 4 位并行加法器为例,串行进位链的进位表达式就可以写为:
如果我们将 C0 的表达式代入 C1,再依次迭代进 C2、C3 ,那么所有进位信号就只依赖于 C-1 了:
并行进位链又称先行进位、跳跃进位,理想的并行进位链就是 n 位全加器的 n 个进位全部同时产生,但实际实现会有困难。一般会使用分组的方式来进行实现,小组内的进位同时产生,小组之间则采用串行进位,这种方式可以总结为“组内并行、组间串行”。
例如,对于 16 位的并行全加器,我们可以 4 位分为一组,得到并行进位链如下:
当 n 越来越大时,随着分组的增加,组间串行进位的延迟时间也会越来越长。解决策略是可以采用多重分组,让“小组”间的进位也同时产生,而“大组”间采用串行进位。当有两个层级的分组时,称为“双重分组跳跃进位”;与之对应,之前只有一个层级的分组方式,被称为“单重分组跳跃进位”。
5. 算术逻辑单元(ALU)
针对每一种算术运算,都必须有相对应的基本硬件配置,其核心部件就是加法器和寄存器;而当需要完成逻辑运算时,又必须配置相应的逻辑电路。将这些功能结合起来,就构成了“算术逻辑单元”(ALU)。
ALU 是一种能进行多种算术运算和逻辑运算的组合逻辑电路,ALU的核心是“带标志加法器”,基本结构如下所示。
其中 A 和 B 是两个 n 位操作数输入端;Cin 是进位输入端;ALUop 是操作控制端,用来控制 ALU 所执行的处理操作。例如,ALU~op~ 选择 Add 运算,ALU 就执行加法运算,输出的结果就是 A 加 B 之和。ALU~op~ 的位数决定了操作的种类。例如,当位数为3时,ALU最多只有8种操作。
F 是结果输出端,此外,还输出相应的标志信息(ZF、OF、SF、CF);在ALU进行加法运算时,可以得到最高位的进位 Cout 。
2.3.2 定点数的移位运算
移位运算根据操作对象的不同,可以分为算术移位和逻辑移位。算术移位针对的是有符号数,逻辑移位针对的是机器码,可以看作无符号数。
1. 算术移位
算术移位的对象是有符号数,有符号数在计算机中采用补码表示。算术移位的特点是,移位后符号位保持不变;空出的位置根据正负和左右移位的情况,决定补 0 还是 1。
-
对于正数,由于 [ x ]原 = [ x ]补 = 真值,因此移位后的空位均补 0。
-
对于负数,算术左移时,高位移出,低位补 0;算术右移时,低位移出,高位补 1。
可见,不论是正数还是负数,移位后其符号位均不变。
例如,假设机器字长为 8,[4]补 = 0000 0100,[-4]补 = 1111 1100;
-
将 4 算术左移一位,就得到了 0000 1000 = [8]补;算术右移一位,就得到了 0000 0010 = [2]补;
-
将 -4 算术左移一位,就得到了 1111 1000 = [-8]补;算术右移一位,就得到了 1111 1110 = [-2]补;
对于有符号数,左移一位若不产生溢出,相当于乘以2 (与十进制数左移一位相当于乘以10类似); 右移一位,若不考虑因移出而舍去的末位尾数,相当于除以2。
2. 逻辑移位
逻辑移位不考虑符号位。
移位规则:逻辑左移时,高位移出,低位补 0;逻辑右移时,低位移岀,高位补 0 。
2.3.3 定点数的加减运算
加减法运算是计算机中最基本的运算,由于减法可以看成是负值的加法,因此计算机中使用补码表示有符号数之后,可以将减法运算和加法运算合并在一起讨论。
1. 补码的加减运算
补码加减运算的规则简单,易于实现。补码加减运算的公式如下(设机器字长为 n): [A + B]补 = [A]补 + [B]补 (mod 2n) [A - B]补 = [A]补 + [-B]补 (mod 2n)
补码运算的特点如下:
-
按二进制运算规则运算,逢二进一。
-
如果做加法,两数的补码直接相加;如果做减法,则将被减数加上减数的机器负数。
-
符号位与数值位一起参与运算,加、减运算结果的符号位也在运算中直接得出。
-
最终将运算结果的高位丢弃,保留 n 位,运算结果也是补码。
例如,假设机器字长为 8 (n = 8),那么
[5]补 = 0000 0101,[4]补 = 0000 0100;
[-5]补 = 1111 1011,[-4]补 = 1111 1100;
[5 + 4]补 = 0000 0101 + 0000 0100 = 0000 1001 = [9]补;
[5 - 4]补 = [5 + (-4)]补 = 0000 0101 + 1111 1100 = 1 0000 0001 = [1]补;
[4 - 5]补 = [4 + (-5)]补 = 0000 0100 + 1111 1011 = 1111 1111 = [-1]补;
[-5 - 4]补 = [-5 + (-4)]补 = 1111 1011 + 1111 1100 = 1 1111 0111 = [-9]补;
2. 补码加减运算电路
利用带标志的加法器电路,可实现补码加减运算。
当控制端信号 Sub 为 0 时,做加法,Sub 控制多路选择器将 ${B}$ 输入加法器,实现 $A + B = [A]补 + [B]补$。当控制端 Sub 为 1 时,做减法,Sub 控制多路选择器将 $\overline{B}$ 的非输入加法器,并将 Sub 作为低位进位送到加法器,实现 $A + \overline{B} + 1 = [A]补 + [-B]补$。
无符号整数的二进制表示相当于正整数的补码表示,因此,该电路同时也能实现无符号整数的加/减运算。可通过标志信息对运算结果进行不同的解释。
-
零标志 ZF = 1 表示结果 F 为 0。不管对于无符号整数还是有符号整数运算,ZF 都有意义。
-
溢出标志 OF = 1 表示有符号数运算时发生溢出。对于无符号数运算,OF 没有意义。
-
符号标志 SF = 1 表示有符号数运算结果为负;有符号数运算结果为正时 SF = 0。对于无符号数运算,SF 没有意义。
-
进/借位标志 CF 表示无符号整数运算时的进/借位,判断是否发生溢出。做加法时,CF = 1 表示结果溢出,因此 CF 等于进位输岀 C~out~。做减法时,CF = 1 表示有借位,即不够减,故 CF 等于进位输出 C~out~ 取反。综合可得 CF = Sub ㊉ C~out~。对于有符号数运算,CF 没有意义。
在之前的例子中,如果表示的是无符号数,那么:
[5 + 4]补 = 0000 0101 + 0000 0100 = [5]补 + [4]补 = 0000 1001 = [9]补; (加法不溢出)
[5 - 4]补 = 0000 0101 + 1111 1100 = [5]补 + [252]补 = 1 0000 0001 = [1]补; (加法溢出、减法不溢出)
[4 - 5]补 = 0000 0100 + 1111 1011 = [4]补 + [251]补= 1111 1111 = [255]补; (加法不溢出、减法溢出)
[-5 - 4]补 = 1111 1011 + 1111 1100 = [251]补 + [252]补 = 1 1111 0111 = [247]补; (加法溢出)
3. 溢出判别方法
溢出 是指运算结果超出了数的表示范围。通常,大于能表示的最大正数称为正上溢,小于能表示的最小负数称为负上溢。仅当两个符号相同的数相加,或两个符号相异的数相减才可能产生溢出。
在之前的例子中,如果假设机器字长为 4(n = 4),能表示的有符号数范围为 -8 ~ 7,那么就有:
[5]补 = 0101,[4]补 = 0100;
[-5]补 = 1011,[-4]补 = 1100;
[5 + 4]补 = 0101 + 0100 = 1001 = [-7]补; (正溢出)
[5 - 4]补 = [5 + (-4)]补 = 0101 + 1100 = 1 0001 = [1]补;
[4 - 5]补 = [4 + (-5)]补 = 0100 + 1011 = 1111 = [-1]补;
[-5 - 4]补 = [-5 + (-4)]补 = 1011 + 1100 = 1 0111 = [7]补; (负溢出)
补码加减运算的溢出判断方法有以下 3 种:
(1)采用一位符号位。
由于减法运算在机器中是用加法器实现的,减法可以看作一个正数和一个负数的加法;因此无论是加法还是减法,只要参加操作的两个数符号相同,结果又与原操作数符号不同,就表示结果溢出。
比如上例中,一正一负相加必然不会溢出;两正数相加得到一个负数(符号位为1),则正溢出;两负数相加得到一个正数,则负溢出。
在实际应用中,为了节省时间,通常可以直接判断符号位产生进位 C~s~ 与最高数位的进位 C~1~ 。如果相同说明没有溢出,否则说明发生溢出。溢出标志 OF = C~s~ ㊉ C~1~。
(2)采用双符号位。
运算结果的两个符号位相同,表示未溢出;运算结果的两个符号位不同,表示溢出,此时最高位就代表真正的符号。也就是说,符号位 S~1~S~2~ = 00 表示结果为正数,无溢出; S~1~S~2~ = 11 表示结果为负数,无溢出。 S~1~S~2~= 01 表示结果正溢出; S~1~S~2~ = 10 表示结果负溢出。溢出标志 OF = S~1~ ㊉ S~2~。
比如上例中,如果采用双符号位,机器字长就应该扩展为 5,那么:
[5]补 = 00 101,[4]补 = 00 100;
[-5]补 = 11 011,[-4]补 = 11 100;
[5 + 4]补 = 00 101 + 00 100 = 01 001 = [1]补; (正溢出)
[5 - 4]补 = [5 + (-4)]补 = 00 101 + 11 100 = 1 00 001 = [1]补;
[4 - 5]补 = [4 + (-5)]补 = 00 100 + 11 011 = 11 111 = [-1]补;
[-5 - 4]补 = [-5 + (-4)]补 = 11 011 + 11 100 = 1 10 111 = [-1]补; (负溢出)
2.2.4 定点数的乘法运算
乘除运算的原理难度较大,考查的概率也较低,做基本了解即可。
在计算机中,乘法运算由累加和右移操作实现。根据机器数的不同,可分为原码一位乘法和补码一位乘法。原码一位乘法的规则比补码一位乘法的规则简单。
1. 原码一位乘法
原码乘法运算的符号位与数值位分开计算。
-
确定乘积的符号位。由两个乘数的符号进行异或运算得到。
-
计算乘积的数值位。两个乘数的数值部分之积,可看作两个无符号数的乘积。
原码一位乘法的基本思路,就是类似竖式乘法的做法,让被乘数 x 分别乘以乘数 y 的每一位,然后再做叠加。不过竖式乘法需要做连加运算,这在电路实现上会有一些困难;改进的做法是,借鉴进制转换的“重复相乘/除法”,对每一位进行迭代计算。
回忆一下二进制数转换成十进制数的重复相乘/除法:
整数部分从高到低,将每一位乘以基数值、再加上后一位,进行“重复相乘”:
(11011)2 = (((1 × 2 + 1) × 2 + 0 ) × 2 + 1) × 2 + 1 = 27
小数部分从低到高,将每一位除以基数值、再加上前一位,进行“重复相除”:
(0.101)2 = ((1 ÷ 2 + 0) ÷ 2 + 1 ) ÷ 2 + 0 = 0.625
所以,两数相乘时,就可以把乘数 y 用这种方式按每一位拆开,并乘以 x 、再逐位叠加就可以了。由于每次乘以 2 就相当于左移一位、除以 2 就相当于右移一位,因此只需要反复迭代这样的 移位 和 加法 运算就可以很容易地实现乘法了。
以纯小数为例,已知 [ x ]~原~ = x~0~ . x~1~x~2~...x~n~,[ y ]~原~ = y~0~ . y~1~y~2~...y~n~,那么
原码一位乘法的运算规则如下:
-
被乘数和乘数均取绝对值|x| 和 |y|参加运算,看作无符号数,符号位为 x~0~ ㊉ y~0~。
-
乘数的每一位 y~i~ 乘以被乘数 |x| 得到 |x| · y~i~,将该结果与前面所得的结果相加,作为部分积;初始值为 0。
-
从乘数的最低位 y~n~ 开始判断:若 y~n~ = 1,则部分积加上被乘数 |x|,然后右移一位;若 y~n~ = 0,则部分积加上 0,然后右移一位。
-
重复上一步骤,判断 n 次。
由于参与运算的是两个数的绝对值,因此运算过程中的右移操作均为逻辑右移。
例如,当 x = 0.1101 = (0.8125)~10~,y = 0.1011 = (0.6875)~10~ 时,计算 x · y。
最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:
x · y = 0.1101 × 0.1011 = 0.10001111 = (0.55859375)~10~
2. 无符号数乘法运算电路
下面是实现两个 32 位无符号数乘法运算的逻辑结构图。
X、Y 均为 32 位寄存器,开始时分别用来存放被乘数 x 、乘数 y ;P 也是 32 位寄存器,用来存放乘积(部分积),初始值置 0。部分积和被乘数做无符号数加法时,可能产生进位,因此还需要一个专门的进位位 C。 C~n~ 为计数器,初始值为 32,此后每进行一轮运算就减 1。
每一轮运算都由寄存器 Y 的最低位 y~n~ 来控制具体逻辑:如果 y~n~ = 1,那么就让 ALU 对乘积寄存器 P 和被乘数寄存器 X 的内容做“无符号加法”运算,运算结果送回寄存器 P,进位存放在 C 中;如果 y~n~ = 0 则不做相加。无论是否做了加法,每一轮运算都会对进位位 C、乘积寄存器 P 和乘数寄存器 Y 实现同步的“逻辑右移”;此时,进位位 C 移入寄存器 P 的最高位,寄存器 Y 的最低位 y~n~ 被移出。每次从寄存器 Y 移出的最低位 y~n~ 都被送到控制逻辑,以决定被乘数是否被加到部分积上。
最终,当原始乘数所有位全部被移出时,寄存器 P 和 Y 就分别存放了乘积的高 32 位和低 32 位。在 CPU 的运算器中,就分别对应了累加器 ACC 和乘商寄存器 MQ。
3. 补码一位乘法
原码一位乘法容易理解,规则也比较简单;缺点是符号位不参与计算,需要单独判断。更重要的是,在计算机中为了方便做加减计算,数据一般是用补码来表示的;这就使得如果我们采用原码乘法,需要计算前先把补码转换成原码,计算结束后再把结果的原码转换为补码,增加了额外的运算。
补码一位乘法 是一种带符号数的乘法,采用相加/相减的校正操作,直接计算补码数据的乘积。
补码乘法是直接对补码进行的。对于纯整数,补码表达为:
而类似的,纯小数补码定义为:
所以,当取不同的正负符号时,补码表达会有所不同,继而影响到逐位相乘叠加的效果。
已知 [ x ]~补~ = x~0~ . x~1~x~2~...x~n~,[ y ]~补~ = y~0~ . y~1~y~2~...y~n~,那么需要分不同的情况讨论:
① 被乘数 x 和乘数 y 符号均为正,即 x~0~ = y~0~ = 0 时,
[ x ]~补~ = x,[ y ]~补~ = y,所以就有:
类似原码一位乘法,利用移位和加法的叠加,就可以计算出补码的乘积;这也就是最终计算结果的补码。
② 被乘数 x 为正,乘数 y 为负,即 x~0~ = 0,y~0~ = 1 时,
[ y ]~补~ = 1. y~1~y~2~...y~n~ = 2 + y,所以:
那么两数的乘积就可以写成:
这样一个计算结果,它的补码表示为:
可以看到,当乘数为负时,可以把乘数补码 [ y ]~补~ 直接去掉符号位,当成一个正数与 [ x ]~补~ 相乘;得到的结果再加上 [ -x ]~补~ 进行校正。所以这种方法也叫做“校正法”。
例如,当 x = 0.1101 = (0.8125)~10~,y = 1.1011 = (-0.3125)~10~ 时,计算 x · y。
我们可以直接计算 0.1101 × 0.1011 = 0.10001111,再加上 [ -x ]~补~ = 1. 0011,得到 :
0.10001111 + 1. 0011 = 1.10111111 = ( -0.25390625 )~10~
③ 被乘数 x 为负,乘数 y 为正,即 x~0~ = 1,y~0~ = 0 时,
我们可以交换被乘数和乘数,直接按情况②来处理;也可以仔细分析,发现乘数 y 为正数,可以写成
[ y ]~补~ = 0. y~1~y~2~...y~n~ 的形式,同样可以借鉴情况②中的分析和原码一位乘的方法。当两数的补码相乘时:
观察可以发现,与原码一位乘完全类似,补码相乘也可以将乘数展开,逐位进行相乘、右移和叠加。不过需要注意的是,这时由于被乘数 x 是负数,右移时就需要在左侧高位补 1,也就是做算术右移、而不是逻辑右移。
这样一来,算术右移就实现了对真值 x 的“除以 2”操作,最终叠加之后的结果,就是 x · y 的补码了。
例如,当 x = 1.1 = (-0.5)~10~,y = 0.011 = (0.375)~10~ 时,计算 x · y。
最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:
x · y = 1.1 × 0.011 = 1.1101 = ( - 0.1875 )~10~
④ 被乘数 x 和乘数 y 符号均为负,即 x~0~ = y~0~ = 1 时,
通过情况②和③的分析可以看出,当乘数 y 为正时,可以直接按照原码一位乘的方式进行补码乘法,注意需要进行算术右移;而当乘数 y 为负时,则可以先不考虑 y 的符号位,同样按照原码一位乘进行补码乘法,最后的结果要再加上 [ -x ]~补~ 进行校正。
例如,当 x = 1.1 = (-0.5)~10~,y = 1.011 = (-0.625)~10~ 时,计算 x · y。
我们可以直接计算 1.1 × 0.011 = 1.1101,再加上 [ -x ]~补~ = 0.1,得到 :
1.1101 + 0.1 = 1 0.0101 = ( 0.3125 )~10~
可以看出,如果使用双符号位来表示正负,会更加方便。
⑤ Booth算法
以上的 4 种情况需要分别讨论,根据乘数的符号来决定是否需要进行校正,这就导致校正法的逻辑控制电路比较复杂。
如果不考虑操作数的符号,直接用统一的规则来处理所有情况,可以采用 比较法。这种方式是 Booth 夫妇首先提出的,所以又叫 Booth 算法。
当被乘数 x 和乘数 y 符号任意时,按照之前讨论的校正法规则,可以写出一个统一的计算公式:
容易推出,对于纯小数,在 mod 2 的前提下,[ -x ]~补~ = - [ x ]~补~,所以可以进一步推导得到:
令 y~n+1~ = 0,那么就可以得到一个通项系数: d~i~ = y~i+1~ - y~i~ ,上式可以进一步化简为:
这样一来,补码乘法的计算方式就跟原码一位乘完全一样了,只是被乘数每次乘的不再是乘数 y 的每一位 y~i~,而是变成了 d~i~ = y~i+1~ - y~i~ 。这样就有 1、-1 和 0 三种情况,每一次计算都由 d~i~ 来决定部分积叠加的是 [ x ]~补~、[ -x ]~补~ 还是 0;然后再做一位算术右移得到新的部分积。最后一步,需要由 d~0~ = y~1~ - y~0~ 决定是否有叠加项,但不再做位移。
Booth 算法的移位规则如下表所示:
Booth 算法的具体运算规则如下:
① 符号位参与运算,运算的数均以补码表示。
② 被乘数一般取 双符号位 参与运算,部分积取 双符号位,初值为 0,乘数取单符号位。
③ 乘数末尾增加一个“附加位” y~n+l~,初始值为 0。
④ 根据(y~i~,y~i+1~)的取值来确定操作,如上表所示。
⑤ 移位按补码右移规则(算术右移)进行。
⑥ 按照上述算法进行 n + 1 步操作,但第 n + 1 步不再移位,仅根据 y~0~ (符号位)与 y~1~ (第一位数值位)的比较结果做相应的叠加运算。所以总共需要进行 n + 1 次累加和 n 次右移。
例如,当 x = 1.1101 = (-0.1875)~10~,y = 1.1011 = (-0.3125)~10~ 时,计算 x · y。
首先得到 [x]~补~ = 11.1101,[-x]~补~ = 00.0011。具体计算步骤如下:
同样,最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:
x · y = 1.1101 × 1.1011 = 0.00001111 = (0.05859375)~10~
4. 补码乘法运算电路
对于有符号数的乘法运算,可以采用补码一位乘。与原码一位乘类似,我们可以用下面的逻辑结构图来实现补码一位乘法的运算电路:
因为是带符号数运算,所以不需要专门的进位位。由于采用 Booth 算法,还需要一个额外的“附加位” y~n+1~。每轮运算,乘积寄存器 P 和乘数寄存器 Y 实现同步的一位“算术右移”,每次从寄存器 Y 移出的最低位成为新的附加位 y~n+1~,它的前一位则成为 y~n~,它们共同决定叠加项是 [ x ]~补~、[ -x ]~补~ 还是 0。另外由于 ALU 可以对加减法统一操作,所以电路种只需要选择加/减一项 [ x ]~补~ 就可以了。
2.2.5 定点数的除法运算
除法是乘法的逆运算,但不像加减法那样可以直接整合。我们可以先从除法的竖式笔算入手,分析一下除法的具体步骤。
例如,当 x = (-0.1011)~2~ = (-0.6875)~10~,y = (0.1101)~2~ = (0.8125)~10~ 时,计算 x / y。
首先可以看出,商的符号为负,余数的符号为负;其次利用竖式计算绝对值的商:
所以可以得到, x / y = - 0.1101(商)... - 0.00000111(余数) = (- 0.8125 ... - 0.02734375 )~10~
如果完全按照竖式除法的规则,需要心算上商,本质就是由当前被除数减去除数乘以当前权值(第 i 轮就乘以 2^-i^),够减就上商 1,不够就上商 0 。得到的余数补 0 后再作为下一轮的被除数进行计算。这个过程中,每轮除数要乘以权值 2^-i^,相当于右移 i 位,得到的余数左侧全部是 0;如果被除数 x、除数 y 和商都是 4 位,就需要 8 位数据来保存余数,这就显得有些麻烦。
更加简单的做法是,每轮相减除数不变,把余数左移;这样效果一样,而电路实现会更加简单。当然,代价就是得到的余数是经过左移之后的;n 轮计算完成之后,需要再右移 n 位,也就是乘以 2^-n^ 才是真正的余数。每轮的相减,也可以转换成负数补码的加法。
所以跟乘法类似,除法运算在计算机中,是转换成逐位的“累加-左移”操作来实现的,可以分为原码除法和补码除法。
1. 原码一位除法
原码一位除法和原码一位乘法一样,特点是符号位单独处理。商符由两个操作数的符号位做“异或”形成,减法操作用补码加法实现。
同样以小数为例,已知 [ x ]~原~ = x~0~ . x~1~x~2~...x~n~,[ y ]~原~ = y~0~ . y~1~y~2~...y~n~,那么
其中,0 . x~1~x~2~...x~n~ 就是 x 的绝对值,记作 x^^ ;0 . y~1~y~2~...y~n~ 是 y 的绝对值,记作 y^^ 。商符由两数符号位异或得到,商值由两数绝对值相除得到。
小数定点除法对被除数和除数有一定的约束条件:
0 <| 被除数 |≤| 除数 |
实现除法运算时,被除数应不大于除数,并且应该避免被除数和除数为 0。商的位数一般和操作数位数相同。
原码除法中,每轮计算需要用被除数绝对值减去除数绝对值:x^^ - y^^。计算机中减法可以用负数补码的加法实现,所以最终的操作就是:[x^^]~补~ + [- y^^]~补~ 。如果结果为正,说明够减,上商 1,结果作为余数直接左移,并作为下一轮被除数;如果结果为负,说明不够减,上商 0,这时的结果并不是真实的余数。
根据对余数的处理方式不同,又可以分为 恢复余数法 和 不恢复余数法(加减交替法) 两种。
(1)恢复余数法
恢复余数法的特点是:当余数为负时,需要加上除数的绝对值,将其恢复成原本的余数。
由于每次得到的是商的高位,所以每轮计算可以将余数和商同时左移一位;余数加上 [- y^^]~补~ ,判断正负来决定下一位商是 1 还是 0;如果为负,还需要先加上 [y^^]~补~ 恢复余数,然后再做左移。
例如,当 x = (-0.1011)~2~ = (-0.6875)~10~,y = (-0.1101)~2~ = (-0.8125)~10~ 时,计算 x / y。
首先看出,商的符号为正,余数的符号为负。并且得到:
x^^ = 0.1011,y^^ = 0.1101,[y^^]~补~ = 0.1101,[-y^^]~补~ = 1.0011
具体计算过程如下:
所以商值为 x^^ / y^^ = 0.1101;而余数由于经过了 4 次左移,所以最终还应该做 4 次右移才是真正的余数:0.0111 * 2^-4^ = 0.00000111,另外还要注意余数符号为负,所以最终结果为:
x / y = 0.1101