计算机组成原理-6-计算机的运算方法

6. 计算机的运算方法

本章全是重点😅,所以“Δ”只是表示笔记补充了MOOC没有讲到的内容。


本章重点:

  1. 计算机中数的表示【6.16.2】:介绍能够被硬件直接识别和处理的数据类型。也就是计算机指令中,包含对这些数据类型进行处理的指令,所以必须用硬件来实现这些运算。
  2. 计算机的运算方法【6.36.4】:整体思路就是对笔算方法进行改进,主要关心硬件可实现。
  3. 运算器的设计【6.5】:介绍CPU中的算术逻辑运算单元ALU的电路设计。

6.1 机器数的表示

6.1.1 无符号数和有符号数

  我们平常手写的有符号数被称为“真值”,而“机器数”则是数字在计算机中的表示方法。“无符号数”就是没有正负号的整数(默认正数),只需要将无符号数用二进制表示,即可按位存储到寄存器中。寄存器的位数反映无符号数的表示范围,比如8位寄存器的取值范围就是 0 ∼ 255 0\sim 255 0255 (C语言char)、16位寄存器的取值范围就是 0 ∼ 65535 0\sim 65535 065535 (C语言unsigned short)、32位寄存器的取值范围就是 0 ∼ 2 32 − 1 0\sim 2^{32}-1 02321 (C语言unsigned int)。

  有符号数包括“符号部分”、“数值部分”,将“符号部分”的正负号进行数字化表示,然后和“数值部分”分别保存到寄存器中,就是“机器数”。通常使用最高位表示“符号位”,也就是将寄存器的最高位作为符号位,“0”就表示为“正号+”、“1”就表示为“负号-”。下图给出“真值”和“机器数”的关系:

有符号数
将±变成0/1
将0/1变成±
机器数
真值
图6-1 有符号数的“真值”和“机器数”之间的转换
  • 符号位:默认为机器数的最高位,0表示正、1表示负。
  • 数值部分:机器中没有硬件表示小数点,只靠约定。比如对于纯小数来说,符号位后面就是小数点。
  • 位数:寄存器长度有限,所以机器数会受到存储字长的限制。

注:本节只考虑有符号的 整数绝对值小于1的小数。绝对值比1大的小数(如“ 8.4 8.4 8.4”),将在下一节“6.2节-数的定点表示和浮点表示”再进行介绍。

下面介绍四种机器数:原码、反码、补码、移码。主要关心:

  • 机器数与真值的转换。
  • 不同机器数形式之间的转化。
  • 机器数表示的范围与其字长的关系。

6.1.2 有符号数-原码

图6-2 原码的计算公式

  将数值部分写成二进制形式、符号位使用0/1表示就是“原码”,也就是“带符号的绝对值表示”。上面直接给出原码表示法的计算公式,但实际上,上述计算公式是根据下面的思想反推出来的!比如下面直接给出二进制数,写出其原码:

整数:用 逗号 将“符号位”和“数值部分”隔开。

  • x = + 1110 ⟶ [ x ] 原 = 0 , 1110 x=+1110 \longrightarrow [x]_{原}=0,1110 x=+1110[x]=0,1110
  • x = − 1110 ⟶ [ x ] 原 = 1 , 1110 = 推导公式 1 , 0000 + 0 , 1110 x=-1110 \longrightarrow [x]_{原}=1,1110\xlongequal{推导公式}1,0000+0,1110 x=1110[x]=1,1110推导公式 1,0000+0,1110

小数:用 小数点 将“符号位”和“数值部分”隔开。

  • x = + 0.1101 ⟶ [ x ] 原 = 0.1101 x=+0.1101 \longrightarrow [x]_{原}=0.1101 x=+0.1101[x]=0.1101,注意前面的“0”表示数值大小,后面原码的“0”表示正负号
  • x = − 0.1101 ⟶ [ x ] 原 = 1.1101 = 推导公式 1.0000 + 0.1101 x=-0.1101 \longrightarrow [x]_{原}=1.1101\xlongequal{推导公式}1.0000+0.1101 x=0.1101[x]=1.1101推导公式 1.0000+0.1101

注:假设数值部分为4位。

来看几个例子:

注:下面均假设数值部分为4位。

【例6.1】已知 [ x ] 原 = 1.0011 [x]_{原}=1.0011 [x]=1.0011,求 x x x
解:由定义得 x = 1 − [ x ] 原 = 1 − 1.0011 = − 0.0011 x=1-[x]_{原}=1-1.0011=-0.0011 x=1[x]=11.0011=0.0011

【例6.2】已知 [ x ] 原 = 1 , 1100 [x]_{原}=1,1100 [x]=1,1100,求 x x x
解:由定义得 x = 2 4 − [ x ] 原 = 10000 − 1 , 1100 = − 1100 x=2^4-[x]_{原}=10000-1,1100=-1100 x=24[x]=100001,1100=1100

【例6.3】已知 [ x ] 原 = 0.1101 [x]_{原}=0.1101 [x]=0.1101,求 x x x
解:因为原码符号位为0,表示为正数,所以直接 x = + 0.1101 x=+0.1101 x=+0.1101

【例6.4】求 x = 0 x=0 x=0 的原码?
解:对于小数来说, [ + 0.0000 ] 原 = 0.0000 [+0.0000]_{原}=0.0000 [+0.0000]=0.0000 [ − 0.0000 ] 原 = 1.0000 [-0.0000]_{原}=1.0000 [0.0000]=1.0000
  对于整数来说, [ + 0 ] 原 = 0 , 0000 [+0]_{原}=0,0000 [+0]=0,0000 [ − 0 ] 原 = 1 , 0000 [-0]_{原}=1,0000 [0]=1,0000
  即,“+0”和“-0”的原码不同!

图6-3 使用“原码”计算加法的问题

  原码的特点就是简单、直观。但是用原码作加法时,两个操作数符号不同时,本质上还是要执行减法(如上图)。此时就希望找到一个与负数等价的正数来代替这个负数,使“减”转化成“加”,也就是“补码”。

6.1.3 有符号数-补码

图6-4 补码的计算公式

  “补”这个概念来自于时钟,比如想将6点时钟拨成3点,那么可以逆时针-3小时、或者顺时针+9小时(因为时钟要对12取模),也就是“-3”和“+9”等价!于是 “补码”就是与负数等价的正数,根本思想为,一个正数和一个负数互为补数时,它们绝对值之和即为模数。正数的补数即为其本身,负数加上模数则为其补数。但实际负数的补码,可用原码除符号位外每位取反,末位加1求得。也就是,正数的补码为其自身,负数的补码=反码+1。如下:

整数:用逗号将“符号位”和“数值部分”隔开。

  • x = + 1010 ⟶ [ x ] 补 = 0 , 1010 x=+1010 \longrightarrow [x]_{补}=0,1010 x=+1010[x]=0,1010
  • x = − 1010 ⟶ [ x ] 补 = 10000 + ( − 1110 ) = 1 , 0110 = 发现规律 1 , ( 0101 + 0001 ) = [ x ] 反 + 1 x=-1010 \longrightarrow [x]_{补}=10000+(-1110)=1,0110\xlongequal{发现规律}1,(0101+0001)=[x]_{反}+1 x=1010[x]=10000+(1110)=1,0110发现规律 1,(0101+0001)=[x]+1

小数:用小数点将“符号位”和“数值部分”隔开。

  • x = + 0.1110 ⟶ [ x ] 补 = 0.1110 x=+0.1110 \longrightarrow [x]_{补}=0.1110 x=+0.1110[x]=0.1110,注意前面的“0”表示数值大小,后面原码的“0”表示正负号
  • x = − 0.1110 ⟶ [ x ] 补 = 10.0000 + ( − 1.0010 ) = 1.0010 = 发现规律 1. ( 0001 + 0001 ) = [ x ] 反 + 1 x=-0.1110 \longrightarrow [x]_{补}=10.0000+(-1.0010)=1.0010\xlongequal{发现规律}1.(0001+0001)=[x]_{反}+1 x=0.1110[x]=10.0000+(1.0010)=1.0010发现规律 1.(0001+0001)=[x]+1

下面给出几个例子:

【例6.5】已知 [ x ] 补 = 0.0001 [x]_{补}=0.0001 [x]=0.0001,求 x x x
解:正数的补码为其自身,显然 x = + 0.0001 x=+0.0001 x=+0.0001

【例6.6】已知 [ x ] 补 = 1.0001 [x]_{补}=1.0001 [x]=1.0001,求 x x x
解:由定义得 x = [ x ] 补 − 2 = − ( 10.0000 − 1.0001 ) = − 0.1111 x=[x]_{补}-2=-(10.0000-1.0001)=-0.1111 x=[x]2=(10.00001.0001)=0.1111。或者直接 x = [ 1.0001 − 0.0001 ] 反 = − 0.1111 x=[1.0001-0.0001]_{反}=-0.1111 x=[1.00010.0001]=0.1111,非常便捷。

【例6.7】已知 [ x ] 补 = 1 , 1110 [x]_{补}=1,1110 [x]=1,1110,求 x x x
解:可以由公式反推,也可以直接 x = 1 , [ 1110 − 0001 ] 反 = − 0010 x=1,[1110-0001]_{反}=-0010 x=1,[11100001]=0010

【自定义例】求 0 0 0 的补码?
解:对于小数来说, [ + 0.0000 ] 补 = 0.0000 [+0.0000]_{补}=0.0000 [+0.0000]=0.0000 [ − 0.0000 ] 补 = 1.1111 + 0.0001 = 0.0000 [-0.0000]_{补}=1.1111+0.0001=0.0000 [0.0000]=1.1111+0.0001=0.0000
  对于整数来说, [ + 0 ] 补 = 0 , 0000 [+0]_{补}=0,0000 [+0]=0,0000 [ − 0 ] 补 = 1 , 1111 + 0 , 0001 = 0 , 0000 [-0]_{补}=1,1111+0,0001=0,0000 [0]=1,1111+0,0001=0,0000
  即,“+0”和“-0”的补码相同!

【自定义例】 求真值 − 1.0000 -1.0000 1.0000 的补码?求补码 [ x ] 补 = 1 , 0000 [x]_{补}=1,0000 [x]=1,0000 对应的真值?
解:由小数补码的定义得 [ x ] 补 = 2 + x = 10.0000 − 1.0000 = 1.0000 [x]_{补} = 2+x=10.0000-1.0000=1.0000 [x]=2+x=10.00001.0000=1.0000原码无法表示 − 1.0000 -1.0000 1.0000
  由正数补码的定义得 x = [ x ] 补 − 2 5 = − ( 10 , 0000 − 1 , 0000 ) = − 1 , 0000 = − 2 5 x=[x]_{补}-2^5=-(10,0000-1,0000)=-1,0000=-2^5 x=[x]25=(10,00001,0000)=1,0000=25
  即,只有符号位是1的补码,表示取值范围内最小的幂次!

6.1.4 有符号数-反码

图6-5 反码的计算公式

  反码很简单,就是正数的反码为其自身,负数的反码将其“原码”的“数值部分”按位取反、“符号位”不变。如下:

整数:用逗号将“符号位”和“数值部分”隔开。

  • x = + 1101 ⟶ [ x ] 反 = 0 , 1101 x=+1101 \longrightarrow [x]_{反}=0,1101 x=+1101[x]=0,1101
  • x = − 1101 ⟶ [ x ] 反 = 1 , 0010 x=-1101 \longrightarrow [x]_{反}=1,0010 x=1101[x]=1,0010

小数:用小数点将“符号位”和“数值部分”隔开。

  • x = + 0.1101 ⟶ [ x ] 反 = 0.1101 x=+0.1101 \longrightarrow [x]_{反}=0.1101 x=+0.1101[x]=0.1101,注意前面的“0”表示数值大小,后面原码的“0”表示正负号
  • x = − 0.1010 ⟶ [ x ] 反 = 1.0101 x=-0.1010 \longrightarrow [x]_{反}=1.0101 x=0.1010[x]=1.0101

【例6.8】已知 [ x ] 反 = 0 , 1110 [x]_{反}=0,1110 [x]=0,1110,求 x x x
解:由定义得 x = + 1110 x=+1110 x=+1110

【例6.9】已知 [ x ] 反 = 1 , 1110 [x]_{反}=1,1110 [x]=1,1110,求 x x x
解:由定义得 x = − 0001 x=-0001 x=0001

【例6.10】求 0 0 0 的反码?
解:对于小数来说, [ + 0.0000 ] 反 = 0.0000 [+0.0000]_{反}=0.0000 [+0.0000]=0.0000 [ − 0.0000 ] 反 = 1.1111 [-0.0000]_{反}=1.1111 [0.0000]=1.1111
  对于整数来说, [ + 0 ] 反 = 0 , 0000 [+0]_{反}=0,0000 [+0]=0,0000 [ − 0 ] 反 = 1 , 1111 [-0]_{反}=1,1111 [0]=1,1111
  即,“+0”和“-0”的反码不同!

6.1.5 有符号数-移码

图6-6 补码难以直接比较大小

  如上图,补码表示很难直接判断其真值大小。单纯从寄存器的角度看,不仅所有的负数都比正数大,就连负数也是按照逆序排序的。于是很自然的就想到直接将补码的符号位取反不就可以了!于是给出移码的定义,注意 n n n 是整数的位数(不包括符号位):

图6-7 移码的计算公式

移码和补码的比较

  • x = + 1100100 ⟶ [ x ] 补 = 0 , 1100100 [ x ] 移 = 1 , 1100100 x=+1100100 \longrightarrow [x]_{补}=0,1100100\quad [x]_{移}=1,1100100 x=+1100100[x]=0,1100100[x]=1,1100100
  • x = − 1100100 ⟶ [ x ] 补 = 1 , 0011100 [ x ] 移 = 0 , 0011100 x=-1100100 \longrightarrow [x]_{补}=1,0011100\quad [x]_{移}=0,0011100 x=1100100[x]=1,0011100[x]=0,0011100

移码的特点:

  1. “补码”与“移码”符号位相反。两者的转换非常简单。
  2. 不区分正数和负数“+0”和“-0”的移码相同!
  3. 小数没有移码。在计算机中,“移码”专用于判断,通常用于“浮点数”的阶码部分,所以没有小数的移码定义。
  4. 最小真值的移码为全0。根据第一条,最小真值的补码只有符号位为1。

6.1.6 机器数总结

  下图给出了“原码”、“补码”、“反码”、“移码”的转换关系:

正数相同,负数如图转换
将±变成0/1
符号位不变
数值部分取反
+1
符号位取反
符号位不变
数值部分取反、+1
所有位取反、+1
x原码
x反码
x补码
有符号数的真值x
x移码
-x补码
图6-8 负数的机器数转换关系

正数的机器数都与原码相同,负数有如下转换关系:

  • 【原码–>反码】除符号位外每位取反。
  • 【反码–>补码】末位加1。
  • 【原码–>补码】除符号位外每位取反、末位加1。
  • 【补码–>原码】除符号位外每位取反、末位加1。

注意:【原码–>补码】和【补码–>原码】的转换逻辑相同!

下面给出两个例子:

【例6.11】设机器数字长为8位(其中1位为符号位)。对于整数,当其分别代表无符号数、原码、补码和反码时,对应的真值范围各为多少?
即,补码比原码、反码可以多表示一个负数

【例6.12】 已知 [ y ] 补 [y]_{补} [y],求 [ − y ] 补 [-y]_{补} [y]
解: [ y ] 补 [y]_{补} [y] 连同符号位在内,每位取反,末位加1,即为 [ − y ] 补 [-y]_{补} [y]。这是一个非常重要的规律。

6.2 数的定点表示和浮点表示

6.2.1 定点表示

图6-9 定点表示的格式

  “定点表示”也就是小数点位置固定,要么在第一位后表示“绝对值小于1的小数”,要么在最后一个表示“整数”。上一节已经详细的介绍了。于是在定点机中可以分为如下两种。其中“小数定点机”精度为 2 − n 2^{-n} 2n、“整数定点机”的精度为 1 1 1下表给出了取值范围:

表6-1 定点表示的取值范围

但“定点表示”有很多问题,于是便有了下一小节的“浮点表示”:

  • 编程困难,程序员要调节小数点的位置。
  • 数的表示范围小,为了能表示两个大小相差很大的数据,需要很长的机器字长。比如太阳的质量是 0.2 × 1 0 34 0.2\times 10^{34} 0.2×1034 克,一个电子的质量大约为 0.9 × 1 0 − 27 0.9\times 10^{-27} 0.9×1027 克,两者数量级相差 1 0 61 10^{61} 1061,使用定点表示则相差203位。
  • 数据存储单元的利用率往往很低。

6.2.2 浮点表示

下面是本小节关心的问题:

  • 为什么在计算机中要引入浮点数表示?——定点表示不够用
  • 浮点表示的格式是什么?
  • 尾数和阶码的基值必须是2吗?基值的影响?——不一定,但影响表示精度
  • 表数范围与精度和哪些因素有关?
  • 为什么要引入规格化表示?
  • 目前浮点数表示格式的标准是什么?——IEEE 754

  “浮点表示”的基本思想就是使用 二进制的“科学记数法,比如 N = 11.0101 = 0.110101 × 2 [ 10 ] 二进制 N=11.0101=0.110101\times 2^{[10]_{二进制}} N=11.0101=0.110101×2[10]二进制,注意幂次也是二进制数(规格化数)。由于幂次的阶码本身也是二进制表示,所以幂次的范围很大,这样我们便可以使用有限字长表示数量级相差很大的数字。下面给出浮点数的规格化形式

图6-10 浮点表示的规格化格式
  • j j j:阶码,是一个有符号整数,控制尾数的左右移动。 j f j_f jf 是符号位、其余是数值部分(长度为 m m m)。
  • S S S:尾数,是绝对值小于1的小数、要求尾数规格化 S f S_f Sf 是符号位、其余为数值部分(长度为 n n n)。
  • r r r:尾数的基值,计算机中通常取 2、4、8、16等。基数 r r r 越大,可表示的浮点数的范围越大,但浮点数的精度降低,因为一次移动的位数增加。基数不同,浮点数的规格化形式不同,如下。
  • r = 2 r=2 r=2,“原码规格化”原则是尾数最高位为1;“补码规格化”原则是尾数的符号位和第一数位不同。
  • r = 4 r=4 r=4,“原码规格化”原则尾数最高2位不全为0。
  • r = 8 r=8 r=8,“原码规格化”原则尾数最高3位不全为0。

注:规格化是为了尽可能保证数据精度,防止存储很多无效0。

  浮点数本质上是用用有限的数据表示无限多的实数。显然浮点数的表示范围和 阶码 j j j、尾数 S S S 的取值范围有关。阶码长度影响范围,尾数长度影响精度。如下图,可以看到 m = 4 m=4 m=4 n = 10 n=10 n=10 的浮点数(16位),表示范围为 ± [ 2 − 25 ∼ 2 15 ) \pm[2^{-25}\sim2^{15}) ±[225215)、精度为 2 − 10 2^{-10} 210(十进制):

图6-11 浮点数的表示范围
  • 上溢:阶码>最大阶码,按“计算出错”处理。
  • 下溢:尾数为0、或者阶码<最小阶码,按“机器零”处理。机器零的两种情况:
  1. 浮点数尾数=0时。
  2. 浮点数阶码≤能表示的最小数时。若阶码采用“补码”,最小值是只有最高位为1;若阶码采用“移码”,最小值是阶码全是0。

注:上述机器零的规定有利于机器中“判0”电路的实现。

下面是几个浮点数范围的练习:

【练习】设机器数字长为24位,欲表示±3万的十进制数,试问在保证数的最大精度的前提下,除阶符、数符各取1位外,阶码、尾数各取几位?
解:由 2 14 < 30000 < 2 15 2^{14}<30000<2^{15 } 214<30000<215,阶码取值范围应大于15,于是阶码长度最小为 4 + 1 = 5 4+1=5 4+1=5 位。剩下的19位就是尾数长度。

【例6.13】将 + 19 128 +\frac{19}{128} +12819 写成二进制定点数、浮点数及在定点机和浮点机中的机器数形式。其中数值部分均取10位,数符取1位,浮点数阶码取5位(含1位阶符),尾数规格化。
解:二进制定点数: x = + 19 128 = 10011 × 2 − [ 7 ] 十进制 = 0.00100    11000 x=+\frac{19}{128}=10011\times2^{-[7]_{十进制}}=0.00100\;11\bold{000} x=+12819=10011×2[7]十进制=0.0010011000
  浮点数的规格化形式 x = 0.10011    00000 × 2 − [ 10 ] 二进制 x=0.10011\;00000\times 2^{-[10]_{二进制}} x=0.1001100000×2[10]二进制
  定点机: [ x ] 原 = [ x ] 反 = [ x ] 补 = 0.00100    11000 [x]_{原}=[x]_{反}=[x]_{补}=0.00100\;11000 [x]=[x]=[x]=0.0010011000
  浮点机(原码):阶码为 j = 1 , 0010 j=1,0010 j=1,0010、尾数为 0.10011    00000 0.10011\;00000 0.1001100000
  浮点机(反码):阶码为 j = 1 , 1101 j=1,1101 j=1,1101、尾数为 0.10011    00000 0.10011\;00000 0.1001100000
  浮点机(补码):阶码为 j = 1 , 1110 j=1,1110 j=1,1110、尾数为 0.10011    00000 0.10011\;00000 0.1001100000

【例6.14】将 − 58 -58 58 表示成二进制定点数和浮点数,并写出它在定点机和浮点机中的三种机器数及阶码为移码、尾数为补码的形式(其他要求同上例)。
解:二进制定点数: x = − 111010 = − 00001    11010 x=-111010=-\bold{0000}1\;11010 x=111010=0000111010
  浮点数的规格化形式 x = − 0.11101    00000 × 2 + [ 110 ] 二进制 x=-0.11101\;00000\times 2^{+[110]_{二进制}} x=0.1110100000×2+[110]二进制
  定点机(原码): [ x ] 原 = 1 , 00001    11010 [x]_{原}=1,00001\;11010 [x]=1,0000111010
  定点机(反码): [ x ] 反 = 1 , 11110    00101 [x]_{反}=1,11110\;00101 [x]=1,1111000101
  定点机(补码): [ x ] 补 = 1 , 11110    00110 [x]_{补}=1,11110\;00110 [x]=1,1111000110
  浮点机(原码):阶码为 j = 0 , 0110 j=0,0110 j=0,0110、尾数为 1.11101    00000 1.11101\;00000 1.1110100000
  浮点机(反码):阶码为 j = 0 , 0110 j=0,0110 j=0,0110、尾数为 1.00010    11111 1.00010\;11111 1.0001011111
  浮点机(补码):阶码为 j = 0 , 0110 j=0,0110 j=0,0110、尾数为 1.00011    00000 1.00011\;00000 1.0001100000
  浮点机(要求):阶码为 j = 1 , 0110 j=1,0110 j=1,0110、尾数为 1.00011    00000 1.00011\;00000 1.0001100000

【习题6.16】设机器数字长为16位,写出下列各种情况下它能表示的数的范围。设机器数采用1位符号位,答案均用十进制数表示。
(1) 无符号数。—— 0 ∼ 65535 0\sim 65535 065535
(2) 原码表示的定点小数。—— − ( 1 − 2 − 15 ) ∼ 1 − 2 − 15 -(1-2^{-15})\sim1-2^{-15} (1215)1215
(3) 补码表示的定点小数。—— − 1 ∼ 1 − 2 − 15 -1\sim1-2^{-15} 11215
(4) 补码表示的定点整数。—— − 32768 ∼ 32767 -32768\sim32767 3276832767
(5) 原码表示的定点整数。—— − 32767 ∼ 32767 -32767\sim32767 3276732767
(6) 浮点数的格式为:阶码6位(含1位阶符),尾数10位(含1位数符)。分别写出正数和负数的表示范围。(假设阶码和尾数都是原码、且不用规格化)
  原码非规格化:正数 2 − 9 × 2 − 31    ∼    ( 1 − 2 − 9 ) × 2 31 2^{-9}\times2^{-31}\;\sim\;(1-2^{-9})\times 2^{31} 29×231(129)×231;负数 − ( 1 − 2 − 9 ) × 2 31    ∼    − 2 − 9 × 2 − 31 -(1-2^{-9})\times 2^{31}\;\sim\;-2^{-9}\times2^{-31} (129)×23129×231
(7) 浮点数格式同(6),机器数采用补码规格化形式,分别写出其对应的正数和负数的真值范围。
  补码规格化要求尾数的符号位和第一数位不同,于是其正数最小值为 2 − 1 2^{-1} 21,负数最大值为 − ( 2 − 1 + 2 − 9 ) -(2^{-1}+2^{-9}) (21+29)
  采用 补码规格化:正数 2 − 1 × 2 − 32    ∼    ( 1 − 2 − 9 ) × 2 31 2^{-1}\times2^{-32}\;\sim\;(1-2^{-9})\times 2^{31} 21×232(129)×231;负数 − 1 × 2 31    ∼    − ( 2 − 1 + 2 − 9 ) × 2 − 32 -1\times 2^{31}\;\sim\;-(2^{-1}+2^{-9})\times2^{-32} 1×231(21+29)×232
  若 补码非规格化:正数 2 − 9 × 2 − 32    ∼    ( 1 − 2 − 9 ) × 2 31 2^{-9}\times2^{-32}\;\sim\;(1-2^{-9})\times 2^{31} 29×232(129)×231;负数 − 1 × 2 31    ∼    − 2 − 9 × 2 − 32 -1\times 2^{31}\;\sim\;-2^{-9}\times2^{-32} 1×23129×232

6.2.3 ΔIEEE 754标准

本节参考视频:“王道-计算机组成原理-2.3.2 IEEE 754”——介绍阶码时也用 n n n 表示整数的位数,但是带符号位,相当于MOOC中的 n + 1 n+1 n+1

注:MOOC只是简单介绍了IEEE 754规定的位数,其余的没有详细介绍。

  IEEE 754中规定了浮点数的格式,如下图,基值 r = 2 r=2 r=2阶码使用移码表示、尾数使用原码规格化表示。由于尾数规格化后第一数位必为1,于是便可以将这一位省略,称为“隐藏位”,进一步提升了“浮点数”的精度。下面还给出不同类型数据的的取值范围:

图6-12 浮点数标准格式—IEEE 754
  • 短浮点数:正数 1 × 2 − 126 ∼ ( 1 + 1 − 2 − 23 ) × 2 127 1\times2^{-126}\sim (1+1-2^{-23})\times2^{127} 1×2126(1+1223)×2127、负数 − ( 1 + 1 − 2 − 23 ) × 2 127 ∼ − 1 × 2 − 126 -(1+1-2^{-23})\times2^{127}\sim -1\times2^{-126} (1+1223)×21271×2126
  • 长浮点数:正数 1 × 2 − 1022 ∼ ( 2 − 2 − 52 ) × 2 1023 1\times2^{-1022}\sim (2-2^{-52})\times2^{1023} 1×21022(2252)×21023、负数 − ( 2 − 2 − 52 ) × 2 1023 ∼ − 1 × 2 − 1022 -(2-2^{-52})\times2^{1023}\sim -1\times2^{-1022} (2252)×210231×21022

注:“临时浮点数”没有隐藏位。

  从上述取值范围可以注意到,阶码的移码的偏置值并不是 “6.1.5节-移码” 中的 2 n 2^n 2n,而是 2 n − 1 2^n-1 2n1,也就是正常的移码减一。这是因为 IEEE 754 将阶码的全0、全1规定成特殊状态。所以 单精度浮点数8位阶码的真正范围应该是-126~127。特殊状态介绍如下,以“单精度浮点数”为例:

  • 阶码全0、尾数不全为0:表示非规格化小数 ± ( 0. xxx...xxx ) 二进制 × 2 − 126 \pm (0.\text{xxx...xxx})_{二进制}\times2^{-126} ±(0.xxx...xxx)二进制×2126,也就是隐含最高位变成0。
  • 阶码全0、尾数全0:表示真值 ± 0 \pm 0 ±0,正负号取决于“数符位”。
  • 阶码全1、尾数不全为0:表示非法运算 NaN \text{NaN} NaN,比如 ∞ − ∞ \infty-\infty 0 / 0 0/0 0/0等情况。
  • 阶码全1、尾数全0:表示无穷大 ± ∞ \pm \infty ±,正负号取决于“数符位”。对应“正上溢”/“负上溢”的情况。

快速计算:下面的“阶码”特指 IEEE 754 所规定的阶码的移码表示(偏置值 2 n − 1 2^n-1 2n1)。

  • 原码→阶码:原码 + 2 n − 1 2^n-1 2n1。正数就是先将符号位取反再-1;负数就是将原码的所有位都取反。
  • 阶码→原码:阶码 - ( 2 n − 1 2^n-1 2n1)。先+1、符号取反变成补码,再将补码变换成原码(正数的补码就是原码)。

最后是两个例题:

【王道-例1】将十进制数 -0.75 转换为 IEEE 754 的单精度浮点数格式表示。
解:由 x = − 0.11 = − ( 1.1 ) × 2 − 1 x=-0.11=-(1.1)\times2^{-1} x=0.11=(1.1)×21 可得数符为1,阶码的原码为 1 , 0000    001 1,0000\;001 1,0000001 的所有位都取反就是移码表示 0 , 1111    110 0,1111\;110 0,1111110,尾数为 1000... 1000... 1000...,于是结果为 32’h BF40 0000。

【王道-例2】IEEE 754的单精度浮点数 32’h C0 A0 00 00 的真值是多少?
解:由 1100 0000 1010 0000…得,阶码 1 , 000    0001 1,000\;0001 1,0000001 的原码为 0 , 000    0010 0,000\;0010 0,0000010 也就是 +2,尾数为 1.0100... 1.0100... 1.0100... 也就是 1.25,于是真值为 − 1.25 × 2 2 = − 5.0 -1.25\times 2^{2}=-5.0 1.25×22=5.0

6.3 定点运算

6.3.1 移位运算

1、移位的运算规则

  从数学意义上说,“移位”是改变小数点的位置。但是在机器用语中,“移位”则是小数点不动,移动数值部分。若使用二进制存储,移位就表示按2的倍数进行扩大、缩小。“移位”与“加减”配合,能够实现乘除运算。移位分为“算术移位”和“逻辑移位”,下面是给出移位规则:

图6-13 算术移位规则、逻辑移位规则
  • 算术移位:有符号数的移位。
  • 逻辑移位:无符号数的移位。

上述“补码”的移位规则,本质上是建立在原码的移位规则上,负数“左移填0”是因为“原码”最后的“10000”在“补码”中不会改变;负数“右移填1”是因为补码的高位都是“原码”取反。下面是一些练习:

【例6.16】设机器数字长为8位(含1位符号位),写出 A = + 26 A=+26 A=+26 时,三种机器数左、右移一位和两位后的表示形式及对应的真值,并分析结果的正确性。
解: A = − 11010 A=-11010 A=11010,得 [ A ] 原 = [ A ] 补 = [ A ] 反 = 0 , 001    1010 [A]_{原}=[A]_{补}=[A]_{反}=0,001\;1010 [A]=[A]=[A]=0,0011010,于是

【例6.17】设机器数字长为8位(含1位符号位),写出 A = − 26 A=-26 A=26 时,三种机器数左、右移一位和两位后的表示形式及对应的真值,并分析结果的正确性。
解: A = − 11010 A=-11010 A=11010,得 [ A ] 原 = 1 , 001    1010 [A]_{原}=1,001\;1010 [A]=1,0011010 [ A ] 反 = 1 , 110    0101 [A]_{反}=1,110\;0101 [A]=1,1100101 [ A ] 补 = 1 , 110    0110 [A]_{补}=1,110\;0110 [A]=1,1100110,于是

注:上述两题可以看出,只要没有溢出,计算就非常准确。

2. 移位的硬件实现

  当移位移出了1时,就会发生“溢出”。高位溢出称为“出错”,是因为移位后会和正确结果差距很大。低位溢出称为“影响精度”,这是因为移位后和正确结果的偏差不会超过最小精度。和上述移位规则相对应,下面给出了移位的硬件实现。可以看到,如果移位的添补代码是0,那就直接传入;若填补代码是1,还可以利用符号位。如下图:

图6-14 算术移位的硬件实现、逻辑移位的硬件实现
  • 左侧方框:符号位所在寄存器。
  • 蓝色箭头所在方框:数值部分所在寄存器。

注:为了避免“逻辑左移”将最高位移丢,上右图使用了“带进位的移位”。

6.3.2 加减法运算-补码

1. 补码加减法运算的公式

  进行加法运算时,若采用“原码”,那么正数和负数的加法实际上还是减法,而“补码”就是与负数等价的正数。如下给出计算公式,注意相加时连同符号位一起相加,最后舍弃最高的进位即可:

图6-15 补码加减法运算的公式

注:【例6.12】中提到过“ [ y ] 补 [y]_{补} [y] 连同符号位在内,每位取反,末位加1,即为 [ − y ] 补 [-y]_{补} [y]”。

下面给出几个例子:

【例6.18】设 A = 0.1011 A=0.1011 A=0.1011 B = − 0.0101 B=-0.0101 B=0.0101,求 [ A + B ] 补 [A+B]_{补} [A+B]
解: [ A ] 补 + [ B ] 补 = 0.1011 + 1.1011 = 10.0110 [A]_{补}+[B]_{补}=0.1011+1.1011=10.0110 [A]+[B]=0.1011+1.1011=10.0110,于是舍弃符号位进位即得 [ A + B ] 补 = 0.0110 [A+B]_{补}=0.0110 [A+B]=0.0110

【例6.19】设 A = − 9 A=-9 A=9 B = − 5 B=-5 B=5,求 [ A + B ] 补 [A+B]_{补} [A+B]?(假设数值部分是4位)
解: [ A ] 补 + [ B ] 补 = [ 1 , 1001 ] 补 + [ 1 , 0101 ] 补 = 1 , 0111 + 1 , 1011 = 11 , 0010 [A]_{补}+[B]_{补}=[1,1001]_{补}+[1,0101]_{补}=1,0111+1,1011=11,0010 [A]+[B]=[1,1001]+[1,0101]=1,0111+1,1011=11,0010,于是舍弃符号位进位即得 [ A + B ] 补 = 1 , 0010 [A+B]_{补}=1,0010 [A+B]=1,0010

【例6.20】设机器数字长为8位(含1位符号位),且 A = 15 A=15 A=15 B = 24 B=24 B=24,用补码求 A − B A-B AB
解: [ A ] 补 + [ − B ] 补 = 0 , 000    1111 + [ 1 , 001    1000 ] 补 = 0 , 000    1111 + 1 , 110    1000 = 1 , 111    0111 = [ A − B ] 补 [A]_{补}+[-B]_{补}=0,000\;1111+[1,001\;1000]_{补}=0,000\;1111+1,110\;1000=1,111\;0111=[A-B]_{补} [A]+[B]=0,0001111+[1,0011000]=0,0001111+1,1101000=1,1110111=[AB],于是 A − B = 1 , 000    1001 = [ − 9 ] 十进制 A-B=1,000\;1001=[-9]_{十进制} AB=1,0001001=[9]十进制

【练习1】设机器数字长为5位(含1位符号位),且 x = 9 16 x=\frac{9}{16} x=169 y = 11 16 y=\frac{11}{16} y=1611,用补码求 x + y x+y x+y
解: [ A ] 补 + [ B ] 补 = 0.1001 + 0.1011 = 1.0100 [A]_{补}+[B]_{补}=0.1001+0.1011=1.0100 [A]+[B]=0.1001+0.1011=1.0100,于是得 x + y = 1.1100 = [ − 0.75 ] 十进制 x+y=1.1100=[-0.75]_{十进制} x+y=1.1100=[0.75]十进制。计算错误!

【练习2】设机器数字长为8位(含1位符号位),且 A = − 97 A=-97 A=97 B = + 41 B=+41 B=+41,用补码求 A − B A-B AB
解: [ A ] 补 + [ − B ] 补 = [ 1 , 110    0001 ] 补 + [ 1 , 010    1001 ] 补 = 1 , 001    1111 + 1 , 101    0111 = 10 , 111    0110 [A]_{补}+[-B]_{补}=[1,110\;0001]_{补}+[1,010\;1001]_{补}=1,001\;1111+1,101\;0111=10,111\;0110 [A]+[B]=[1,1100001]+[1,0101001]=1,0011111+1,1010111=10,1110110,于是舍弃符号位进位即得 [ A + B ] 补 = 0 , 111    0110 [A+B]_{补}=0,111\;0110 [A+B]=0,1110110,也就是 A − B = + 118 A-B=+118 AB=+118。计算错误!

注:实际中数值部分字长不是无限的,一定要注意机器数字长,机器数字长不够会导致溢出

2. 溢出的判断

  上面提到补码相加时,需要连通管符号位一起相加,最后舍弃最高的进位。那么如何判断补码溢出呢?如下:

  1. 使用一位符号位判判断:两个操作数一正一负必不可能溢出;但若两个操作数符号相同,其结果的符号与原操作数的符号不同,即为溢出。缺点是需要记录两个进位,然后进行异或。
  • 硬件实现(异或):最高有效位的进位 ⊕ \oplus 符号位的进位 = 1,溢出。
  • 关键点:“最高有效位的进位”会影响“符号位的进位”。
  1. 使用两位符号位判溢出:使用两个符号位,新增符号位与原符号位相同。进行加/减运算后,若结果的双符号位相同,未溢出;若结果的双符号位不同,溢出最高符号位代表其真正的符号
  2. 进位判断法:在进行补码加减运算时,可以记录下进位和借位的情况。如果最高位的进位和次高位的进位不同,则表示发生了溢出。

3. 补码加减法的硬件配置

根据上述原理很容易实现补码加减法的硬件配置,如下图:

图6-16 补码加减法的硬件配置
  • 加法器:核心部件,完成补码的加减法运算。
  • A \text{A} A:也就是 ACC \text{ACC} ACC,保存被加数。
  • X \text{X} X:保存加数。
  • G A \text{G}_{\text{A}} GA G S \text{G}_{\text{S}} GS:加减法标记位。 G A = 1 \text{G}_{\text{A}}=1 GA=1 表示做加法, G S = 1 \text{G}_{\text{S}}=1 GS=1做减法。
  • 求补控制逻辑:减法时,控制 X \text{X} X 求解所保存加数的补码,也就是“取反、+1”。

6.3.3 Δ乘法运算-原码

1. 笔算乘法的分析和改进

  乘法运算可用“加”和“移位”实现。如下左图,给出了手写的二进制乘法计算过程,可以看到其本质上就是不断的“移位+相加”。但向左移动乘数不符合硬件的逻辑,于是进行改进。最重要的改进就是将遍历过程改为,由乘数的末位决定被乘数是否与原部分积相加,然后右移1位形成新的部分积,同时乘数右移1位(末位移丢),空出高位存放部分积的低位。可以看到乘法计算的中间结果不断占用乘数的空间,如下右图所示:

笔算乘法改进


硬件改进
符号位单独处理异或电路
乘数的某一位决定是否加被乘数将乘数放到移位寄存器当中
4个位积一起相加多次累加
乘积的位数扩大一倍两个寄存器
图6-17 根据笔算乘法改进成实际的“原码乘法”

重要的改进内容:

  1. 不断的右移、求和。被乘数只与部分积的高位相加。
  2. 在保留乘法的中间值的同时,顺便蹭一下乘数的空间。
  3. 符号位单独使用异或电路计算。

注:“王道-计算机组成原理-2.2.6.1 原码的乘法运算-11:28~17:40”给出了上述寄存器的动态计算过程。非常形象!

2. 原码乘法—一位乘

  上述改进的笔算乘法就是“原码一位乘算法”。原码乘法的特点:

  1. 乘积的符号位单独处理,数值部分为绝对值相乘。
  2. 用移位的次数判断乘法是否结束。因为有可能不做加法。

注:移位操作均为“逻辑右移”。

【例6.21】已知 x = − 0.1110 x=-0.1110 x=0.1110 y = 0.1101 y=0.1101 y=0.1101,求 [ x ⋅ y ] 原 [x\cdot y]_{原} [xy]
解:写出原码后,符号位直接异或,数值部分如右

3. 原码乘法的硬件配置—一位乘

图6-18 原码一位乘的硬件配置
  • A \text{A} A:n+1寄存器,也就是 ACC \text{ACC} ACC,保存累加和高位(不断移位)。最高位不是符号位,而是保存低位部分相加后的进位。
  • Q \text{Q} Q:n+1寄存器,乘商寄存器。计算刚开始时保存乘数,计算过程不断右移,计算结束后就是乘积的低位。
  • X \text{X} X:n+1寄存器,保存被乘数(无需移位)。
  • 加法器:n+1位加法器
  • 移位和加控制:由乘数的最低位控制。“控制门”可以送0给加法器。
  • 计数器 C \text{C} C:记录移位的次数,判断乘法是否结束。一般是从n递减。
  • S \text{S} S:符号位,将两个操作数的符号位异或得到。
  • G M \text{G}_\text{M} GM:乘法的标志位。

核心部件:三个n+1位寄存器 A \text{A} A Q \text{Q} Q X \text{X} X;一个n+1位全加器。

3. 原码乘法—两位乘

  上述计算过程中的每一步都只使用乘数的一位进行计算。要想提高乘法的运算速度,我们可以同时使用乘数的两位进行计算。具体来说,每次运算的最低两位,有如下明显的四种情况:

  • 00:新部分积等于原部分积右移两位。——容易实现
  • 01:新部分积等于原部分积加被乘数后右移两位。——容易实现
  • 10:新部分积等于原部分积加2倍被乘数后右移两位。——容易实现
  • 11:新部分积等于原部分积加3倍被乘数后右移两位。——先减1倍被乘数,再加4倍被乘数(放到下一次循环就是直接加1)。

注: z z z 表示原有部分积, x ∗ x^* x 表示被乘数的绝对值, y ∗ y^* y 表示乘数的绝对值, → 2 \rightarrow 2 2 表示右移两位, − x ∗ -x^* x 通过补码实现, C j C_j Cj 用于保存上一步是否需要“+4倍的被乘数”。
注:计算过程“部分积”需要3位符号位

  虽然两位乘法可提高乘法速度,但它仍基于重复相加和移位的思想,而且随着乘数位数的增加,重复次数增多,仍然影响乘法速度的进一步提高。采用并行阵列乘法器可大大提高乘法速度。有关阵列乘法器的内容可参见中文教材“附录6B”。

6.3.4 Δ乘法运算-补码

本小节参考视频:“王道-计算机组成原理-2.2.6.2 补码的乘法运算”。

注:MOOC对于本小节完全没有介绍,只有简单的PPT。

1. 补码一位乘—Booth算法

  原码乘法实现比较容易,但由于加减运算都采用补码,若只用原码乘法,需要在做乘法前将补码转换成原码,相乘之后再变换回去,显然非常复杂。于是,补码乘法应运而生,避免了码制的转换。“补码一位乘”中定义了“辅助位”,也就是在 “乘商寄存器 MQ \text{MQ} MQ” 的最低位之后多添加一位,初始为0。由于计算机中寄存器位宽统一,“辅助位”导致 MQ \text{MQ} MQ 变成 n+2 位寄存器,于是 ACC \text{ACC} ACC X \text{\text{X}} X 也都变成了 n+2 位(双符号位)。于是,补码乘法一定要使用双符号位,原码无所谓。补码的一位乘和原码的一位乘非常类似,下面两者的对比:

表6-2 “原码一位乘”和“补码一位乘”的对比
原码一位乘补码一位乘(Booth算法)
核心思想符号位通过异或确定,数值位由被乘数和乘数的绝对值运算得到符号位、数值位都是由被乘数和乘数进行运算得到
初始状态ACC、X、MQ都存放单符号原码ACC、X存放双符号位补码;MQ存放单符号位补码。
运算次数n轮加法、移位n轮的加法、移位,最后多来一次加法
加法可能的操作+0、+[|x|]原+0、+[x]补、+[-x]补
移位类型逻辑移位算术移位
符号位乘数的符号位不参与运算乘数的符号位参与运算(最后一次加法)
加法逻辑·MQ中最低位=1时,(ACC)+[|x|]原
·MQ中最低位=0时,(ACC)+0
·MQ最低两位=01时,(ACC)+[x]补
·MQ最低两位=00/11时,(ACC)+0
·MQ最低两位=10时,(ACC)+[-x]补

来看一道例题:

【王道-例】设机器字长为5位(含1位符号位,n=4), x = − 0.1101 x=-0.1101 x=0.1101 y = + 0.1011 y= +0.1011 y=+0.1011,采用Booth算法求 x ∗ y x*y xy
解: [ x ] 补 = 11.0011 [x]_{补}=11.0011 [x]=11.0011 [ − x ] 补 = 00.1101 [-x]_{补}=00.1101 [x]=00.1101 [ y ] 补 = 0.1011 [y]_{补}=0.1011 [y]=0.1011,于是
[ x ∗ y ] 补 = 11.0111    0001 [x*y]_{补}=11.0111\;0001 [xy]=11.01110001,即 x ∗ y = 1.1001    1111 x*y=1.1001\;1111 xy=1.10011111

注:上图中称 MQ \text{MQ} MQ 中的每一位为 Y s , Y 1 , Y 2 , Y 3 , Y 4 , Y 5 Y_s,Y_1,Y_2,Y_3,Y_4,Y_5 Ys,Y1,Y2,Y3,Y4,Y5 Y 5 Y_5 Y5就是辅助位。
注:上述最后一步可以看出,“补码一位乘”符号位自然形成

2. 补码乘法—两位乘

  和原码两位乘类似,也是将两次乘法的步骤合并到一起进行运算。“补码一位乘”需要一次比较 MQ \text{MQ} MQ 的最低两位,那么“补码两位乘”就需要一次比较 MQ \text{MQ} MQ 的最低三位。如下给出运算规则:

表6-3 补码两位乘的运算规则
  • [ y n − 1 , y n , y n + 1 ] [y_{n-1},y_n,y_{n+1}] [yn1,yn,yn+1] 表示 MQ \text{MQ} MQ 的低三位。
  • z z z 表示部分积。

6.3.5 除法运算-原码

约定

  1. “小数定点机”除法中,被除数小于除数,这样的到的商还是小数。否则溢出。
  2. “整数定点机”除法中,被除数大于除数,这样的到的商就是整数。否则溢出。
  3. 被除数不为0、除数不为0,可以提前通过判断电路规避。

注:上述前两种“溢出”的情况可以见下一节“6.4节-浮点四则运算”。

1. 笔算除法的改进

  二进制除法实际上比十进制除法简单,因为二进制商的每一位只有0/1。如下左图所示,给出了笔算除法的过程。我们已经约定好“被除数小于除数”,于是笔算乘法本质上也是不断地比较被除数和当前余数的大小。商的符号位单独处理、数值部分为绝对值相除。按照硬件逻辑进行改进后,和“原码乘法”相反,“原码除法”是从低位计算到高位。如下:

笔算除法改进


硬件改进
商符单独处理符号位异或电路
心算上商
|x|-|y|>0上商1、|x|-|y|<0上商0
加法电路给出上商
余数不动低位补“0”,减右移一位的除数余数左移一位低位补“0”,减除数
2倍字长加法器1倍字长加法器
上商位置不固定在寄存器最末位上商,然后左移
图6-19 根据笔算除法进行改进-小数定点除法

重要的改进内容:

  1. 不断的左移、求和。每次都与“负的除数绝对值的补码”低位相加。
  2. 不断缩小余数的中间值的空间,最终只剩下商。
  3. 符号位单独使用异或电路计算。

2. 原码除法:恢复余数法

在第 i i i 步:

  1. 上商:若余数 R i > 0 R_i>0 Ri>0,上商1;若余数 R i < 0 R_i<0 Ri<0,上商0。
  2. 恢复余数:若上商1,则跳转到下一步;若上商0,恢复余数 R i + y ∗ R_i+y^* Ri+y
  3. 逻辑移位:将余数逻辑左移1位。
  4. 准备下一次运算:再将现在的余数减去 y ∗ y^* y,得到下一次余数 R i + 1 R_{i+1} Ri+1

特点:若数值部分为n位,则上商n+1次,其中第一次上商判溢出。移位n次。加n+1~2n+1次。用移位的次数判断除法是否结束。

【例6.24】假设机器字长5位(符号位1位),给出 x = − 0.1011 x=-0.1011 x=0.1011 y = − 0.1101 y=-0.1101 y=0.1101,在小数定点机中求 [ x y ] 原 [\frac{x}{y}]_{原} [yx]
解: [ x ] 原 = 1.1011 [x]_{原}=1.1011 [x]=1.1011 [ y ] 原 = 1.1101 [y]_{原}=1.1101 [y]=1.1101 [ y ∗ ] 补 = 0.1101 [y^*]_{补}= 0.1101 [y]=0.1101 [ − y ∗ ] 补 = 1.0011 [-y^*]_{补}= 1.0011 [y]=1.0011。然后如下:

  • 每一步:首先判断上商、若上商0则进行“加法”恢复余数、然后余数“逻辑左移”、最后进行“加法”用于下一次判断。
  • 上图中未展示“第一次上商”,其作用是判断是否溢出、是否符合本节开始提到的“约定”。

注:“王道-计算机组成原理-2.2.6.1 原码的除法运算-10:29~22:20”给出了上述的寄存器动态计算过程。非常形象!

3. 原码除法:加减交替法

  “加减交替法”是对“恢复余数法”的改进,将每一步中的两次加法合并,直接节省掉“恢复余数法”中“恢复余数”的过程。如下:

在第 i i i 步:

  • 若为第一步,直接 R i + 1 = x ∗ + y ∗ R_{i+1}=x^*+y^* Ri+1=x+y
  • 否则,若余数 R i > 0 R_i>0 Ri>0,则上商1,计算新的余数 R i + 1 = 2 R i − y ∗ R_{i+1}=2R_i-y^* Ri+1=2Riy
  • 否则,若余数 R i < 0 R_i<0 Ri<0,则上商0,计算新的余数 R i + 1 = 2 ( R i + y ∗ ) − y ∗ = 2 R i + y ∗ R_{i+1}=2(R_i+y^*)-y^*=2R_i+y^* Ri+1=2(Ri+y)y=2Ri+y
  • 若为最后一步,且上商为0,则最后加法计算的结果不是对应的余数,还需要再 + y ∗ +y^* +y 恢复余数。

特点:若数值部分为n位,则上商n+1次,第一次上商判溢出。移位n次。加(n+1)/(n+2)次。用移位的次数判断除法是否结束。

【例6.25】假设机器字长5位(符号位1位),给出 x = − 0.1011 x=-0.1011 x=0.1011 y = − 0.1101 y=-0.1101 y=0.1101,在小数定点机中求 [ x y ] 原 [\frac{x}{y}]_{原} [yx]
解: [ x ] 原 = 1.1011 [x]_{原}=1.1011 [x]=1.1011 [ y ] 原 = 1.1101 [y]_{原}=1.1101 [y]=1.1101 [ x ∗ ] 补 = 0.1011 [x^*]_{补}= 0.1011 [x]=0.1011 [ y ∗ ] 补 = 0.1101 [y^*]_{补}= 0.1101 [y]=0.1101 [ − y ∗ ] 补 = 1.0011 [-y^*]_{补}= 1.0011 [y]=1.0011。然后如下:

4. 加减交替法的硬件配置

图6-20 原码加减交替除法的硬件配置
  • A \text{A} A:n+1寄存器,也就是 ACC \text{ACC} ACC。一开始保存被除数,计算过程不断左移,最后就是余数。最高位不是符号位,而是保存高位部分相加后的进位。
  • Q \text{Q} Q:n+1寄存器,乘商寄存器。计算刚开始时保存0,计算过程不断右移,计算结束后就是商。
  • X \text{X} X:n+1寄存器,保存除数(无需移位)。
  • 加法器:n+1位加法器
  • 移位和加控制逻辑:最低位控制加减交替。“控制门”可以送 + y ∗ +y^* +y − y ∗ -y^* y 给加法器。
  • 计数器 C \text{C} C:记录移位的次数,判断除法是否结束。
  • S \text{S} S:符号位,将两个操作数的符号位异或得到。
  • G D \text{G}_\text{D} GD:除法的标志位。
  • V \text{V} V:表示是否溢出。

6.3.6 Δ除法运算-补码

本小节参考视频:“王道-计算机组成原理-2.2.7.2 补码的除法运算”。

注:MOOC完全没有涉及。

  补码除法也分“恢复余数法”和“加减交替法”,后者用得较多,在此只讨论补码除法的“加减交替法”。补码除法中,符号位直接参与运算。被除数/余数、除数均采用双符号位。如下为计算步骤:

若要计算 x / y x/y x/y,在第 i i i 步:

  • 若为第一步,判断 被除数 [ x ] 补 [x]_{补} [x] 和 除数 [ y ] 补 [y]_{补} [y] 的符号位,同号则 R i = [ x ] 补 + [ − y ] 补 R_i=[x]_{补}+[-y]_{补} Ri=[x]+[y];异号则 R i = [ x ] 补 + [ y ] 补 R_i=[x]_{补}+[y]_{补} Ri=[x]+[y]
  • 否则,若 余数 R i R_i Ri 和 除数 [ y ] 补 [y]_{补} [y] 同号,则上商1,计算新的余数 R i + 1 = 2 R i + [ − y ] 补 R_{i+1}=2R_i+[-y]_{补} Ri+1=2Ri+[y]
  • 否则,若 余数 R i R_i Ri 和 除数 [ y ] 补 [y]_{补} [y] 异号,则上商0,计算新的余数 R i + 1 = 2 R i + [ y ] 补 R_{i+1}=2R_i+[y]_{补} Ri+1=2Ri+[y]
  • 若为最后一步,直接上商为1,结束算法。无需“恢复余数”。

特点:若数值部分为n位,则上商n次,第一次上商判溢出,最后一次直接置1。移位n次。加n+1次。用移位的次数判断除法是否结束。

下面是一个例题:

【王道-例】设机器字长为5位(含1位符号位,n=4), x = + 0.1000 x=+0.1000 x=+0.1000 y = − 0.1011 y=-0.1011 y=0.1011,采用补码加减交替除法求 x / y x/y x/y
解: [ x ] 补 = 00.1000 [x]_{补}=00.1000 [x]=00.1000 [ y ] 补 = 11.0101 [y]_{补}=11.0101 [y]=11.0101 [ − y ] 补 = 00.1011 [-y]_{补}=00.1011 [y]=00.1011
即商 [ x / y ] 补 = 1.0101 [x/y]_{补}=1.0101 [x/y]=1.0101,余数 0.0111 × 2 − 4 0.0111\times 2^{-4} 0.0111×24,两者都是补码表示。

表6-3 除法运算总结
除法类型符号位
是否参与运算
加减次数上商次数移位上商、加减原则其他
原码恢复余数法n+1~2n+1次n+1左移n次余数的正负——
原码加减交替法n+1或n+2n+1左移n次余数的正负若最终上商为0,需恢复余数
补码加减交替法n+1n左移n次余数和除数是否同号商末位横置为1

6.4 浮点四则运算

6.4.1 浮点数的加减运算

1. 浮点数加减运算的基本步骤

  假设浮点数基数为2,并且使用“补码”计算,下面直接直接给出“浮点加减运算”的基本步骤:

  1. 对阶:首先用补码求阶差 [ Δ j ] 补 = [ j x ] 补 − [ j y ] 补 [\Delta_j]_{补}=[j_x]_{补}-[j_y]_{补} [Δj]=[jx][jy],然后“小阶向大阶看齐”。小阶所在的浮点数阶码增加、尾数算数右移。注意“右移”只是可能影响数据精度,而“左移”可能会出现特别大的错误。
  2. 尾数求和:补码定点求和,注意要重复一位符号位
  3. 尾数规格化:非规格化时,检查尾数的两个符号位,若为 10 / 01 10/01 10/01 则“右规”;否则就“左规”。每次移位都是算术移位,同时需要调整阶码。尾数溢出则“右规”。
  • 原码规格化原则是第一数位为1。
  • 补码规格化原则是符号位和第一数位不同。

注:若计算结果溢出(参考“6.2.2 浮点表示”),下溢全部按照机器零处理。上溢会进入“出错处理”。

2. 舍入方法和溢出判断

  需要注意的是,上述“对阶”和“规格化”过程中,只要进行尾数右移时,丢失有效数字就会引起误差,于是便有下面的舍入方法

  1. 截断法:直接移出。后续没有任何操作。上述“计算步骤”中的默认方法。
  2. 0舍1入法:移出的是1,移动后,就在最末尾加1。可能会使尾数再次溢出。类似于“四舍五入”。
  3. 恒置“1”法:只要右移,就强制使末位为1。

计算完成之后再判断是否溢出。“溢出判断”较为简单,只要计算结果超出范围就是溢出。“上溢”需要进入“出错处理”,“下溢”则按照“机器零”处理:

  1. 阶码判断法:阶码为0,判断为下溢,按照“机器零”处理。若阶码采用补码,且为补码的最小负数(符号位为1、其余位都是0),判定为上溢,进入“出错处理”。
  2. 尾数判断法:尾数为0,就判定为下溢,按照“机器零”处理。

最后给出两个例题,理解浮点数的加减运算过程:

【例6.27】 x = 0.1101 × 2 10 x= 0.1101\times 2^{10} x=0.1101×210 y = 0.1011 × 2 01 y= 0.1011\times 2^{01} y=0.1011×201,求 x + y x+y x+y?(除阶符、数符外,阶码取3位,尾数取6位)
解: [ x ] 补 = 00 , 010 ;    00.110100 [x]_{补}=00,010;\;00.110100 [x]=00,010;00.110100 [ y ] 补 = 00 , 001 ;    00.101100 [y]_{补}=00,001;\;00.101100 [y]=00,001;00.101100

  1. 对阶:阶差为1,则 y y y 阶码+1、尾数算数右移1位, [ y ] 补 ′ = 00 , 010 ;    00.010110 [y]_{补'}=00,010;\;00.010110 [y]=00,010;00.010110。(没有误差)
  2. 尾数求和: [ S x ] 补 + [ S y ] 补 ′ = 00.110100 + 00.010110 = 01.001010 [S_x]_{补}+[S_y]_{补'}=00.110100+00.010110=01.001010 [Sx]+[Sy]=00.110100+00.010110=01.001010
  3. 尾数规格化:补码规格化,尾数溢出则“右规”1位、阶码+1,得结果 [ x + y ] 补 = 00 , 011 ;    00.100101 [x+y]_{补}=00,011;\;00.100101 [x+y]=00,011;00.100101,于是 x + y = 00 , 011    00.100101 = + 37 64 × 2 3 x+y=00,011\;00.100101=+\frac{37}{64}\times 2^{3} x+y=00,01100.100101=+6437×23。(准确结果)

【例6.28】 x = − 5 8 × 2 − 5 x= -\frac{5}{8}\times 2^{-5} x=85×25 y = 7 8 × 2 − 4 y= \frac{7}{8}\times 2^{-4} y=87×24,求 x − y x-y xy?(除阶符、数符外,阶码取3位,尾数取6位)
解: [ x ] 补 = [ 1 , 101 ;    1.101000 ] 补 = 11 , 011 ;    11.011000 [x]_{补}=[1,101;\;1.101000]_{补}=11,011;\;11.011000 [x]=[1,101;1.101000]=11,011;11.011000 [ y ] 补 = [ 1 , 100 ;    0.111000 ] 补 = 11 , 100 ;    00.111000 [y]_{补}=[1,100;\;0.111000]_{补}=11,100;\;00.111000 [y]=[1,100;0.111000]=11,100;00.111000

  1. 对阶:阶差为-1,则 x x x 阶码+1、尾数算数右移1位, [ x ] 补 ′ = 11 , 100 ;    11.101100 [x]_{补'}=11,100;\;11.101100 [x]=11,100;11.101100。(没有误差)
  2. 尾数求和: [ S x ] 补 ′ + [ − S y ] 补 = 11.101100 + 11.001000 = 110.110100 [S_x]_{补'}+[-S_y]_{补}=11.101100+11.001000=110.110100 [Sx]+[Sy]=11.101100+11.001000=110.110100,舍弃符号位进位得 10.110100 10.110100 10.110100
  3. 尾数规格化:补码规格化,尾数溢出则“右规”1位、阶码+1,得结果 [ x − y ] 补 = 11 , 101 ;    11.011010 [x-y]_{补}=11,101;\;11.011010 [xy]=11,101;11.011010,于是 x − y = 11 , 011 ;    11.100110 = − 19 32 × 2 − 3 x-y=11,011;\;11.100110=-\frac{19}{32}\times 2^{-3} xy=11,011;11.100110=3219×23。(准确结果)

6.4.2 Δ浮点数的乘除运算

  “浮点数乘除法”符号阶码和尾数分开按类似于定点的运算然后合并。下面直接给出步骤:

  1. 阶码运算:乘法则阶码相加、除法则阶码相减。会判断是否溢出。
  2. 尾数运算:若上一步无溢出,则可以采用任意一种定点小数的乘/除法运算。
  3. 尾数规格化。

注:进行上述运算前会首先判断,排除乘法中有0、被除数为0、除数为0的特殊情况。
注:MOOC和王道均没有提及本节,想进一步学习可以看中文教材的例题。

6.5 算术逻辑单元

6.5.1 ALU电路

  前面已经介绍了定点运算、浮点运算、算术运算、逻辑运算的硬件电路,实际上这些运算都被集成到一个集成电路芯片中,也就是ALU(Arithmetic and Logic Unit,算逻运算单元)。ALU是组合逻辑电路,若需要保存结果,还需要额外添加寄存器。ALU再和“控制器”集成在一起,就构成了计算机系统的CPU。如下左图是ALU电路的表示符号,其中 k i k_i ki 控制ALU进行“算术运算”还是“逻辑运算”:

图6-21 ALU电路符号、74181外特性

但ALU只能实现“1位”的算逻运算,要完成“多位”的算逻运算还要将几个ALU集成在一起,如上右图74181中就集成了4个ALU,可以完成4位的算逻运算。下面给出74181的功能表:

表6-4 74181 ALU的算数/逻辑运算功能表

6.5.2 快速进位链

  那上述74181内部是如何连接多个ALU的呢?本节以“加法器”为例,介绍算术运算、逻辑运算的电路优化问题。根据“数字电子技术基础”的执行,要实现n位加法运算,可以使用n+1个“全加器”构成的一个“并行加法器”。之所以称之为“并行”,是因为两个n+1的数,可以利用该加法器以并行的方式完成加法运算。如下图所示,每个 FA i \text{FA}_i FAi 都是一个全加器:

图6-22 并行加法器示意图
  • 和的计算: S i = A ‾ i B ‾ i C i − 1 + A ‾ i B i C ‾ i − 1 + A i B ‾ i C ‾ i − 1 + A i B i C i − 1 S_i=\overline{A}_i\overline{B}_iC_{i-1}+\overline{A}_iB_i\overline{C}_{i-1}+A_i\overline{B}_i\overline{C}_{i-1}+A_iB_iC_{i-1} Si=AiBiCi1+AiBiCi1+AiBiCi1+AiBiCi1
  • 进位的计算: C i = A ‾ i B i C i − 1 + A i B ‾ i C i − 1 + A i B i C ‾ i − 1 + A i B i C i − 1 = A i B i + ( A i + B i ) C i − 1 C_i=\overline{A}_iB_iC_{i-1}+A_i\overline{B}_iC_{i-1}+A_iB_i\overline{C}_{i-1}+A_iB_iC_{i-1}=A_iB_i+(A_i+B_i)C_{i-1} Ci=AiBiCi1+AiBiCi1+AiBiCi1+AiBiCi1=AiBi+(Ai+Bi)Ci1

但显然,依照上图的连接方式,每个“全加器”都需要等待上一级“进位”输入,才能输出本级的计算结果。每一级全加器的进位前后连接,形成“进位链”。于是,并行加法器的运算瓶颈在于“进位链”

并行进位链
拆分成多个小组
小组内并行、小组间串行
拆分成若干大组、若干小组
组内并行、组件串行
进位计算暴力展开
双重分组跳跃进位链
32位加法需要10ty
单重分组跳跃进位链
32位加法需要20ty
普通的并行进位链
串行进位链
32位加法需要64ty
图6-23 进位链的改进思路

上图给出进位的链的改进思路及指标,下面来依次介绍。假设级延迟时间“与非门/或门” t y t_y ty、“与或非门” 1.5 t y 1.5t_y 1.5ty

1. 串行进位链

  前面所示的“并行加法器”结构实际上就是采用了“串行进位链”,进位逐级产生。我们可以将进位的计算专门抽出来,给出如下的进位递推公式

C i = A i B i + ( A i + B i ) C i − 1 = 令 t i = A i + B i 令 d i = A i B i d i + t i C i − 1 = 德 ⋅ 摩根定律 d i ‾ ⋅ t i C i − 1 ‾ ‾ C_i=A_iB_i+(A_i+B_i)C_{i-1}\xlongequal[令t_i=A_i+B_i]{令d_i=A_iB_i}d_i+t_iC_{i-1}\xlongequal{德·摩根定律}\overline{\overline{d_i}\cdot\overline{t_iC_{i-1}}} Ci=AiBi+(Ai+Bi)Ci1di=AiBi ti=Ai+Bidi+tiCi1摩根定律 ditiCi1

  • 本地进位: d i = A i B i d_i=A_iB_i di=AiBi
  • 传递进位: t i = A i + B i t_i=A_i+B_i ti=Ai+Bi

上述两者均与外来进位无关。

图6-24 四位串行进位链示意图

假设“与非门”的级延迟时间为 t y t_y ty,当所有的 d i d_i di t i t_i ti 形成后,4位全加器需要 8 t y 8t_y 8ty 产生全部进位, n n n 位全加器需要 2 n t y 2nt_y 2nty 产生全部进位

2. 并行进位链-普通版

  “并行进位链”的宗旨就是“同时产生”n位加法器的进位,被称为先行进位/跳跃进位。最直观的想法就是根据进位的递推公式,将所有进位的计算直接暴力展开,于是 所有的进位都可以直接产生

C 0 = d 0 + t 0 C − 1 C 1 = d 1 + t 1 C 0 = d 1 + t 1 d 0 + t 1 t 0 C − 1 C 2 = d 2 + t 2 C 1 = d 2 + t 2 d 1 + t 2 t 1 d 0 + t 2 t 1 t 0 C − 1 C 3 = d 3 + t 3 C 2 = d 3 + t 3 d 2 + t 3 t 2 d 1 + t 3 t 2 t 1 d 0 + t 3 t 2 t 1 t 0 C − 1 \begin{aligned} & C_0 = d_0+t_0C_{-1}\\ & C_1 = d_1+t_1C_{0} = d_1+t_1d_0+t_1t_0C_{-1}\\ & C_2 = d_2+t_2C_{1} = d_2+t_2d_1+t_2t_1d_0+t_2t_1t_0C_{-1}\\ & C_3 = d_3+t_3C_{2} = d_3+t_3d_2+t_3t_2d_1+t_3t_2t_1d_0+t_3t_2t_1t_0C_{-1}\\ \end{aligned} C0=d0+t0C1C1=d1+t1C0=d1+t1d0+t1t0C1C2=d2+t2C1=d2+t2d1+t2t1d0+t2t1t0C1C3=d3+t3C2=d3+t3d2+t3t2d1+t3t2t1d0+t3t2t1t0C1

图6-25 四位一组并行进位链

假设级延迟时间“与非门/或门” t y t_y ty、“与或非门” 1.5 t y 1.5t_y 1.5ty,当所有的 d i d_i di t i t_i ti 形成后,优点是不管有多少进位,只要“与或非门”能连接足够的输入,n位全加器只需 2.5 t y 2.5t_y 2.5ty 就能产生全部进位(上图中n=4)。缺点是电路连接非常复杂,一般也就做到上述4位。于是便给出下面的折衷方式。

3. 并行进位链-单重分组跳跃进位链

  “单重分组跳跃进位链”的思路是将n位全加器分若干小组,小组中的进位同时产生,小组与小组之间采用串行进位。每一个小组内部,都采用上述普通版的并行进位链:

图6-26 单重分组跳跃进位链-16位

根据前面假设,当所有的 d i d_i di t i t_i ti 形成后,4位全加器只需 2.5 t y 2.5t_y 2.5ty 就能产生全部进位,于是上述16位全加器需要 10 t y 10t_y 10ty 就能产生全部进位(串行进位链需要 32 t y 32t_y 32ty)。

4. 并行进位链-双重分组跳跃进位链

  “双重分组跳跃进位链”的思路是将n位全加器分若干大组,大组中又包含若干小组。每个大组中,小组的最高位进位同时产生,小组间的进位同时产生,大组与大组之间采用串行进位。对于单个大组来说,第一重进位由各个小组完成,但注意高位的小组(如5、6、7)都需要等待第二重小组计算完毕才能计算进位。每个小组本质上都是前面介绍的普通版“并行进位链”,只不过不计算最高位的进位,而是生成 D i D_i Di T i T_i Ti (生成方式如下公式)。小组间的连接方式如下图,假设每个小组都是4位加法器:

C 3 = d 3 + t 3 d 2 + t 3 t 2 d 1 + t 3 t 2 t 1 d 0 + t 3 t 2 t 1 t 0 C − 1 = 令 T 8 = t 3 t 2 t 1 t 0 令 D 8 = d 3 + t 3 d 2 + t 3 t 2 d 1 + t 3 t 2 t 1 d 0 = D 8 + T 8 C − 1 C 7 = D 7 + T 7 C 3 = D 7 + T 7 D 8 + T 7 T 8 C − 1 C 11 = D 6 + T 6 C 7 = D 6 + T 6 D 7 + T 6 T 7 D 8 + T 6 T 7 T 8 C − 1 C 15 = D 5 + T 5 C 11 = D 5 + T 5 D 6 + T 5 T 6 T 7 + T 5 T 6 T 7 D 8 + T 5 T 6 T 7 T 8 C − 1 \begin{aligned} C_3 &= d_3+t_3d_2+t_3t_2d_1+t_3t_2t_1d_0+t_3t_2t_1t_0C_{-1} \xlongequal[令T_8=t_3t_2t_1t_0]{令 D_8=d_3+t_3d_2+t_3t_2d_1+t_3t_2t_1d_0}=D_8+T_8C_{-1}\\ C_7 &= D_7+T_7C_{3} = D_7+T_7D_8+T_7T_8C_{-1}\\ C_{11} &= D_6+T_6C_{7} = D_6+T_6D_7+T_6T_7D_8+T_6T_7T_8C_{-1}\\ C_{15} &= D_5+T_5C_{11} = D_5+T_5D_6+T_5T_6T_7+T_5T_6T_7D_8+T_5T_6T_7T_8C_{-1}\\ \end{aligned} C3C7C11C15=d3+t3d2+t3t2d1+t3t2t1d0+t3t2t1t0C1D8=d3+t3d2+t3t2d1+t3t2t1d0 T8=t3t2t1t0=D8+T8C1=D7+T7C3=D7+T7D8+T7T8C1=D6+T6C7=D6+T6D7+T6T7D8+T6T7T8C1=D5+T5C11=D5+T5D6+T5T6T7+T5T6T7D8+T5T6T7T8C1

  • i i i 小组的本地进位: D i = d 3 + t 3 d 2 + t 3 t 2 d 1 + t 3 t 2 t 1 d 0 D_i=d_3+t_3d_2+t_3t_2d_1+t_3t_2t_1d_0 Di=d3+t3d2+t3t2d1+t3t2t1d0
  • i i i 小组的传送条件: T i = t 3 t 2 t 1 t 0 T_i=t_3t_2t_1t_0 Ti=t3t2t1t0

上述两者均与外来进位无关,且在本小组内连线固定。

图6-27 双重分组跳跃进位链-32位

当所有的 d i d_i di t i t_i ti C − 1 C_{-1} C1 形成后,有如下的计算步骤:

  • 2.5 t y 2.5t_y 2.5ty 时刻:所有小组计算所有的 D i D_i Di T i T_i Ti;第8小组额外计算出自己的进位 C 2 C_2 C2 C 1 C_1 C1 C 0 C_0 C0
  • 5 t y 5t_y 5ty 时刻:第二大组计算出第二重的四个进位 C 3 C_3 C3 C 7 C_7 C7 C 11 C_{11} C11 C 15 C_{15} C15
  • 7.5 t y 7.5t_y 7.5ty 时刻:第4、5、6、7小组计算出自己的所有进位;第一大组计算出第二重的四个进位 C 19 C_{19} C19 C 23 C_{23} C23 C 27 C_{27} C27 C 31 C_{31} C31
  • 10 t y 10t_y 10ty 时刻:第1、2、3小组计算出自己的所有进位。

总结一下,使用双重分组跳跃进位链,32位全加器只需要 10 t y 10t_y 10ty 就能产生全部进位,而“单重分组的并行进位链”需要 20 t y 20t_y 20ty、“串行进位链”需要 64 t y 64t_y 64ty

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虎慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值