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

6.1 数据在计算机中的表示方法

由计算机的硬件决定,任何存储于计算机中的数据,其本质都是以二进制码存储。


根据冯~诺依曼提出的经典计算机体系结构框架。一台计算机由运算器,控制器,存储器,输入和输出设备组成。其中运算器,只有加法运算器,没有减法运算器(据说一开始是有的,后来由于减法器硬件开销太大,被废了 )


所以,计算机中的没法直接做减法的,它的减法是通过加法来实现的。你也许会说,现实世界中所有的减法也可以当成加法的,减去一个数,可以看作加上这个数的相反数。当然没错,但是前提是要先有负数的概念。这就为什么不得不引入一个该死的符号位。


而且从硬件的角度上看,只有正数加负数才算减法。 正数与正数相加,负数与负数相加,其实都可以通过加法器直接相加。

原码、反码、补码的产生过程就是为了解决,计算机做减法和引入符号位(正号和负号的问题)


在了解什么是原码、反码、补码前我们先了解什么无符号数和有符号数

6.1.1 无符号数和有符号数

1. 无符号数

没有正负之分

例C语言中 unsigned int 类型


寄存器的位数反映无符号数的表示范围

  • 8位 0~255
  • 16位 0~65535


2. 有符号数

有符号数包含符号部分和数值部分

一般我们规定有符号数机器数的第1位为符号位,1代表这个数是负数,0代表这个数是整数


假设已C语言中 char类型举例(默认为有符号数)

char类型为1Byte,8bit

那么char类型能表示的范围是多少?

应该是-128~127

要知道为什么是-128~127我们就需要知道数据在计算机中是怎么存储的,下面引入原码、反码、补码以及移码的概念


6.1.2 原码

原码:是最简单的机器数表示方法。用最高位表示符号位,‘1’表示为负号,‘0’表示为正号,其他位存放该数的二进制的绝对值

  • 若以有符号数的四位二进值数为例

1010:最高位为‘1’,表示这是一个负数

其他三位为‘010’,转换为十进制为:2

所以1010表示十进制为:-1

  • 若以无符号数的四位二进值数为例

1010:最高位是‘1’还是‘0’无所谓了,因为它表示为无符号数

所以直接转换为十进制为:10


下图给出部份正负数数的二进制原码表示法

OK,原码表示法很简单有没有,虽然出现了+0和-0,但是直观易懂。 于是,我们高兴的开始运算。

0001+0010=0011 (1+2=3)OK

0000+1000=1000 (+0+(-0)=-0) 额,问题不大

0001+1001=1010 (1+(-1)=-2) ???

噢,1+(-1)=-2,我趣里的

于是我们可以看到其实正数之间的加法通常是不会出错的,因为它就是一个很简单的二进制加法。

而正数与负数相加,或负数与负数相加,就要引起莫名其妙的结果,这都是该死的符号位引起的。0分为+0和-0也是因他而起。

所以原码,虽然直观易懂,易于正值转换。但用来实现加减法的话,运算规则总归是太复杂。于是反码来了。


6.1.3 反码

我们知道,原码最大的问题就在于一个数加上他的相反数不等于零。

例如:

  • 0001+1001=1010 (1+(-1)=-2)
  • 0010+1010=1100 (2+(-2)=-4)

于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那我们干脆用一个正数按位取反来表示负数试试。

反码:

正数的反码还是等于原码

负数的反码就是他的原码除符号位外,按位取反。

若以带符号位的四位二进制数为例:

  • 3是正数,反码与原码相同,则可以表示为0011
  • -3的原码是1011,符号位保持不变,低三位(011)按位取反得(100) 所以-3的反码为1100


下图给出部分正负数的二进制数反码表示法

对着上图,我们再试着用反码的方式解决一下原码的问题


  • 0001+1110=1111 (1+(-1)= - 0)
  • 互为相反数相加等于0,解决。虽然是得到的结果是1111也就是-0


好,我们再试着做一下两个负数相加(运算中均为反码)

  • 1110(-1)+1101(-2)=1011(-4)

噢,好像又出现了新问题

  • (-1)+(-2)=(-4)?

不过好像问题不大,因为1011(是-4的反码,但是从原码来看,它其实是-3。巧合吗?)


我们再看个例子吧

  • 1110(-1)+1100(-3)=1010(-5)

确实是巧合,看来相反数问题是解决了,但是却让两个负数相加的出错了。


但是实际上,两个负数相加出错其实问题不大。我们回头想想我们的目的是什么?是解决做减法的问题,把减法当成加法来算

两个正数相加和两个负数相加,其实都是一个加法问题,只是有无符号位罢了,而正数+负数才是真正的减法问题。

也就是说只要正数+负数不会出错,那么就没问题了。负数加负数出错没关系的,负数的本质就是正数加上一个符号位而已。


但是我们还是不满足为什么 0001+1110=1111 (1+(-1)=-0) 为什么是-0呢?

下面有请补码!


6.1.4 补码

补码:

正数的补码等于他的原码

负数的补码等于反码+1。 (这只是一种算补码的方式,多数书对于补码就是这句话)


OK,补码就讲完了。再见!!!

莫名其妙有没有,为什么补码等于反码加1,为什么……………….?

其实上面那段话,只是补码的求法,而不是补码的定义。很多人以为求补码就要先求反码,其实并不是。

那些鸡贼的计算机学家,并不会心血来潮的把反码+1就定义为补码。只不过是补码正好就等于反码加1罢了。


下面详细介绍补码的思想,可以跳过,你只需要记住补码怎么计算就行。但是如果你肯停下来仔细想想,绝对会觉得非常美妙,极其优雅!


补码的思想

补码的思想就类似于生活中的时钟

如果现在时针停在10点钟,那么怎样让时钟调整到八点钟的位置呢?

有两种方法:

  1. 时钟逆时针往后拨2个小时
  2. 时钟顺时钟往前拨10个小时

也就是说时间正拨10小时,或是倒拨2小时都是八点钟。

也就是10-2=8,而且 MOD[(10+10),12] = 8

既然是等效的,那在时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于12,也称为同余数

这就是补码所谓模运算思想的生活例子


在这里,我们再次强调原码,反码,补码的引入是为了解决做减法的问题。在原码,反码表示法中,我们把减法化为加法的思维是减去一个数,等于加上一个数的相反数,结果发现引入了符号位,却因为符号位造成了各种意向不到的问题。

但是在补码表示法中,我们可以看到其实减去一个数,对于数值有限制,有溢出的运算(模运算)来说,其实也相当于加上这个数的同余数。

也就是说,我们不引入负数的概念,就可以把减法当成加法来算(模运算)

补码的思想介绍完毕


下图给出带符号位四位二进制的补码表示法

到这里,我们发现原码,反码的问题,补码基本解决了。

在补码中也不存在负零了,因为1000表示-8

这是因为根据上面的补码图,做减法时,0001(1)+1111(-1)=0000 ,我们再也不需要一个1000来表示负0了,就把它规定为-8


为什么这样求补码

然后我们再来看看为什么负数的补码的求法为什么是反码+1

假设一个四位负数为1001,即(-1)

  • 反码:1110
  • 绝对值:|-1|=1,即(0001)

1110+0001+1

=1111+1

=10000,即四位数的模(指四位二进制数所能表示的最大数 16)


立即推:负数的反码 + 负数的绝对值 + 1 = 模

而:负数的补码是它绝对值的同余数,即补码 + 负数的绝对值 = 模

立即推:补码 + 负数的绝对值 = 负数的反码 + 负数的绝对值 + 1

得:补码 = 负数的反码 + 1

妙不妙(这玩意是怎么研究出来的)


为什么这里一直在说负数呢?

因为正数的原码、反码、补码都一样,不用计算


到这里补码的概念也介绍完了,我们回到刚才的问题:为什么C语言中char类型的范围为:-128~127

直接上图解:

如果是unsigned char 那能表示范围为:0~255


补码计算的小技巧

如果我们把-8当成负数的原点。那么-5的补码是多少呢?

  • -5 = -8 + 3
  • -5 的补码就是 -8 的补码加 3 ,1000(-8) +0011(3)=1011(-5)

所以完全可以口算出-5的补码是1011


对于八位加法器的话,可以把-128当补码原点。十六位可以把-32768当补码原点。

是的,128是256(八位二进制数的模)的一半,32768是65536(十六位二进数的模)的一半

也很方便有没有,而且简单的是

补码原点总是最高位是‘1’,其他位是‘0’


所以看上图的-128(10000000) 就可以当作补码的原点,-127 就是-128+1,即(10000001)

数学之美


6.1.5 移码

移码(又叫增码或偏置码)通常用于表示浮点数阶码,其表示形式与补码相似,只是其符号位用“1”表示正数,用“0”表示负数,数值部分与补码相同。

  • 例如: n=5时
  • 当X=+3,则[X]移=10011
  • 当X=-3, 则[X]移=01101


移码与补码的关系: [X]移与[X]补的关系是符号位互为相反数(仅符号位不同),

  • 例如:
  • X= + 01011 [X]补 = 01011 [X]移 = 11011
  • X=-11011 [X]补 = 10101 [X]移 = 00101

所以,移码很简单,不管正负数,只要将其补码的符号位取反即可。

即知道补码,将其符号位取反就是移码

知道移码将其符号位取反就是补码


移码的意义

补码表示很难直接判断其真值大小,比如:

我们加一个偏移量:2^5,得到正确的大小比较结果

移码的定义

如果是有符号数,去除符号位

真值、补码、移码的对照表

移码的特点

从这也可以看出上文中C语言中char类型能表示的最小值为啥是:-128

char类型默认是有符号数,1Byte 8bit,第1位是符号位,所以真值的位数n= 7

最小值为:-27


6.1.6 原码、反码、补码、移码总结

上面叭叭说了一大段,总结下来就几条。但是如果你肯停下来琢磨是什么原理,你会发现数学的美妙


  • 对于无符号数
  1. 原码、反码、补码相同;原码 = 将真值转换为二进制
  2. 移码只需要在补码前+1


  • 对于有符号正数
  1. 原码、反码、补码相同;原码 = 将真值转换为二进制
  2. 移码只需要将补码符号位取反


  • 对于有符号数负数
  1. 原码 = 将真值转换为二进制,第1位为符号位,‘1’表示负数,‘0’表示正数
  2. 反码 = 将原码符号位不动,其余为按位取反
  3. 补码 = 反码 + 1
  4. 移码只需要将补码符号位取反

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

6.2.1 定点数表示

小数点固定在某一位置的数称为定点数,有以下两种格式

采用定点数表示方式的机器称为定点机


在这种表达方式中,小数点的位置固定不变,一般用来表示一个纯小数或者整数

数的定点表示熟知的取值范围有限,表示一个纯小数时,小数点固定在符号位之后;表示一个整数时,小数点固定在数据最后一位之后。

纯小数即(0,1)之间的小数


小数定点机中数的表示范围为:-(1-2^-n)~(1-2^-n)

整数定点机中数的表示范围为:-(2^n - 1)~(2^n - 1)


定点法表示整数

例:假设机器数字长为8为,符号位为1位,用定点法表示10和-10

10用二进制表示为:1010

因为字长为8为所以高位补0

则10用定点法表示为:01010000

-10用定点法表示为: 11010000

定点法表示纯小数

例:假设机器数字长为8为,符号位为1位,用定点法表示 0.5 和 -0.5

0.5 = 1/2^1 即小数点向左移1位

所以二进制形式为:0.1

又因为字长为8

所以二进制形式为:0.1000000

对于 -0.5只需将符号位取反 即 1.1000000


6.2.2 浮点数表示

定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。

所以使用浮点数表示的原因:

定点数的表示范围小,为了能表示两个大小相差很大的数据,需要很长的机器字长,导致数据存储单元的利用率往往很低

最终,绝大多数现代的计算机系统采纳了所谓的浮点数表达方式。

这种表达方式利用科学计数法来表达实数,即用一个 尾数,一个 基数,一个 阶数以及一个 符号位来表达实数。

比如 123.45 用十进制科学计数法可以表达为 (-1)^0 x 1.2345 × 10^2 ,其中 (-1)^0的0表示符号位(备注:如果表示负数,符号位为1),1.2345 为尾数,10 为基数,2 为阶数。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。


计算机中的浮点数表示

浮点数的表示范围

这个计算公式看起来比较复杂,其实就是计算特定数取得最大时的数据范围,仔细分析就可以得出来了,没啥好纠结的。

例题:

问题:设机器数字长为 24 位,欲表示± 3 万的十进制数, 试问在保证数的最大精度的前提下,除阶符、数符各 取 1 位外,阶码、尾数各取几位?


分析:

  1. 尾数反应了浮点数的最大精度,所以我们尽可能使尾数n的位数最大。
  2. 阶码决定了浮点数的范围;因为 2^14 < 3000 < 2^15;所以为表示±3万的十进制数,阶数m >=15
  3. 15使用二进制表示为1111;所以机器字长最少占用4位,机器数字长总共为 24 位,除去两个符号位,因此尾数n的位数最大为18位。

浮点数的规格化

为什么要进行浮点数的规格化?


对浮点数进行规格化的主要目的是尽可能的保证数据的精度。

如果不进行规格化,尾数的小数点后边可能会有若干个0,0占据了尾数表示有效数据(不为0的数据)的字长,导致可以表示有效数据的字长变短,降低数据的精度。

因此,我们对浮点数进行规格化,就要使尾数中表示有效数据的位置尽可能的多。

浮点数的规格化形式


比如

  • r = 2 尾数最高位为 1
  • r = 4 尾数最高 2 位不全为 0 ——> 2位二进制数表示一位4进制数
  • r = 8 尾数最高 3 位不全为 0 ——> 3位二进制数表示一位8进制数
  • ...

因此,基数不同,浮点数的规格化形式不同,规格化形式不同,基数 r 越大,浮点数的精度降低


如何进行规格化?

对浮点数进行规格化实际上就是把尾数部分多余的0移除掉,通过操作阶数来保证原来的数值(真值)不变,这种方式称为做规。


浮点数表示例题:

  • 问题一

写成二进制定点数、浮点数及在定点机和浮点机中的机器数形式。其中数值部分均取 10 位,数符取 1 位,浮点数阶码取 5 位(含1位阶符),尾数规格化


解:设x=

二进制形式: x = 0.0010011 ——> 10011(19) / 2^7(128) ——>小数点向左移7位

定点表示: x = 0.0010011 000 ——> 数值部分均取 10 位,所以扩展3位0

浮点规格化形式 :

——> -10(2)为二进制码,表示向右移动两位小数点

  • 问题二

将 –58 表示成二进制定点数和浮点数, 并写出它在定点机和浮点机中的三种机器数及阶码为移码、尾数为补码的形式(其他要求同问题一)。

机器零

  • 当浮点数 尾数为 0 时,不论其阶码为何值按机器零处理
  • 当浮点数 阶码等于或小于它所表示的最小数 时,不论尾数为何值,按机器零处理


6.3 定点四则运算

6.3.1 移位运算

移位运算的意义

移位运算在日常生活中常见。例如:

  • 15米可写作1500厘米,单就数字而言,1500相当于小数点左移了两位,并在小数点前面添了两个0;
  • 同样15也相当于1500相对于小数点右移了两位,并删去了小数点后面的两个0。
  • 可见,当某个十进制数相对于小数点左移n位时,相当于该数乘以10^n;右移n位时,相当于该数除以10^n。

计算机中小数点的位置是事先约定的,因此,二进制表示的机器数在相对于小数点作n位左移或右移时,其实质就便该数乘以或除以2^n(n=1,2...n)。


移位运算又叫移位操作,对计算机来说,有很大的实用价值,例如,当计算机没有乘(除)运算线路时,可以采用移位和加法相结合,实现乘(除)运算。

  • 在计算机中,移位与加减配合,能够实现乘除运算


计算机中机器数的字长往往是固定的,当机器数左移n位或右移n位时,必然会使其n位低位或n位高位出现空位。那么,对空出的空位应该添补0还是1呢?

这与机器数采用有符号数还是无符号数有关,对有符号的移位叫算术移位


算术移位的规则

  • 对于正数,由于 [x]原=[x]补=[x]反=真值 ,故移位后出现的空位均以0添之。
  • 对于负数,由于原码、补码和反码的表示形式不同,故当机器数移位时,对其空位的添补规则也不同。

下表列出了三种不同码制的机器数(整数或小数均可),分别对应正数或负数,移位后的添补规则。

必须注意的是:不论是正数还是负数,移位后其符号位均不变,这是算术移位的重要特点。

乍看可能觉得很唬人,其实你只要会计算原码、反码、补码就行,把原码移位后,计算出对应的反码和补码就行;谨记算数移位符号位不变!!!

例题:

  • 设机器数字长为8位(含一位符号位),若A=±26,写出三种机器数左、右移一位和两位后的表示形式及对应的真值,并分析结果的正确性。

解:

(1)当A = +26 = +11010(二进制码),机器数字长为8位,则 [A]原=[A]补=[A]反=0,0011010

移位结果表示如下:

可见,对于正数,三种机器数移位后符号位不变,左移时最高数位丢1,结果出错;右移时最低数位丢1,影响精度。


(2)当 A = -26 = -11010(二进制码),三种机器数移位结果示于下表

原码

反码

补码

下图示意了机器中实现算术左移和右移操作的硬件框图

算术移位和逻辑移位的区别

有符号数的移位称为算术移位,无符号数的移位称为逻辑移位。


逻辑移位的规则是:

  • 逻辑左移时,高位移出,低位添0;
  • 逻辑右移时,低位移出,高位添0。

符号位跟着一起移

算术移位符号位不动

例如

  • 寄存器内容为01010011,逻辑左移为1010010,算术左移为00100110(最高数位“1”移丢)。
  • 又如寄存器内容为10110010,逻辑右移为01011001。若将其视为补码,算术右移为11011001。

显然,两种移位的结果是不同的。


上例中为了避免算术左移时最高数位丢1,可采用带进位(Cy)的移位,其示意图如下图所示。算术左移时,符号位移至Cy,最高数位就可避免移出。

6.3.2 加减运算

减法运算是计算机中最基本的运算,因减法运算可看作被减数加上一个减数的负值,即A-B=A+(-B),故在此将机器中的减法运算和加法运算合在一起讨论。

现代计算机中都采用补码作加减法运算。

补码加减运算基本公式

补码加法的基本公式为:

即补码表示两个数在进行加法运算时,可以把符号位与数位同等处理,只要结果不超出机器能表示的数值范围,运算后的结果按2^(n+1)取模(对于整数);或按2取模(对于小数),就能得到本次加法的运算结果。


对于减法,因 A-B=A+(-B),则 [A-B]补=[A+(-B)]补

由补码加法基本公式可得:

因此,若机器数采用补码, 当求A-B时, 只需先求[-B]补(称[-B]补为“求补”后的减数),就可按补码加法规则进行运算。而[-B]补由[B]补连同符号位在内,每位取反,末位加1而得。


例:A=0.1010,B=-0.0101,用补码的加法求A+B

解:

[A]补=0.1011,[B]补=1.1011

[A]补+[B]补 = 0.1011+1.1011=0.0110(按模2的意义,最左边的1丢掉),所以 A+B=0.0110


例:x=0.1001,y=-0.0011,用补码的减法求x-y

解:

[x]补=0.1001,[y]补=1.1101,[-y]补=0.0011

[x]补-[y]补=[x]补+[-y]补=0.1001+0.0011=0.1100,所以 x-y=0.1100


例:设机器数字长为8位,其中一位为符号位,令A=-93,B=+45,求[A-B]补。

解:

由A=-93=-1011101,得[A]补=1,0100011,由B=+45=+0101101,得[B]补=0,0101101,[-B]补=1,1010011

[A-B]补=[A]补+[-B]补=1,0100011+1,1010011=10,1110110

按模2^(n+1)的意义,最左边的“1”自然丢掉,故[A-B]补=0,1110110,还原成真值得A-B=118,结果出错,这是因为A-B=-138超出了机器字长所能表示的范围。在计算机中,这种超出机器字长的现象,叫溢出。为此,在补码定点加减运算过程中,必须对结果是否溢出作出明确的判断。


溢出判断

1. 一位符号判断溢出

  • 对于加法,只有在正数加正数和负数加负数两种情况下才可能出现溢出,符号不同的两个数相加是不会出现溢出的。
  • 对于减法,只有在正数减负数或负数减正数两种情况下才可能出现溢出,符号相同的两个数相减是不会出现溢出的。

因此在判断溢出时可以根据参加运算的两个数据和结果的符号位进行。

两个符号位相同的补码相加,如果和的符号位加数的符号相反,则表明运算结果溢出;

两个符号位相反的补码相减,如果差的符号位被减数的符号位相反,则表明运算结果溢出。

原理:

和的符号位发生改变肯定是数值部分进位上来的

或者再想想,两个负数相加的和,它的符号位发生改变岂不是正数

负数 + 负数 = 正数? 我趣里的

这种方法需要判断操作是加法还是减法,以及运算结果与操作数的符号关系。


2. 符号位和数值部分的最高位判溢出

利用数据编码的最高位(符号位)和次高位(数值部分的最高位)的进位状况来判断运算结果是否发生了溢出。

两个补码数实现加减运算时,若最高数值位向符号位的进位值与符号位产生的进位输出值不相同,则表明加减运算产生了溢出


因为当x和y均为n+1位正整数时,其和有两种情况:

  • 当x+y<2^n时,不会发生溢出;
  • 当x+y≥2^n时符号位没有进位,表明发生溢出。

当x和y都是n+1位负数时,其和也有两种情况:

  • 当x+y≥-2^n时,不会发生溢出;
  • 当x+y<-2^n时,符号位相加后变成0并且有进位,而数值部分的最高位相加时无进位,结果变为正数,表明发生了溢出。

例:设x=+1011, y=+1001,求[x+y]补。

解:

[x]补=01011, [y]补=01001

[x+y]补=01011+01001=10100

两个正数相加,最高两位的进位为01,表示发生了溢出,其结果为负数,显然是错误的。


例:设x=-1101,y=-1011,求[x+y]补。

解:

[x]补=10011, [y]补=10101

[x+y]补=10011+10101=01000

两个负数相加,最高两位的进位为10,表示发生了溢出,其结果为正数,显然是错误的。


3. 采用双符号位补码进行判断

正常时两个符号位的值相同,在运算结果中当两个符号位不同时则表明发生了溢出。

运算结果的符号位为01表明两个正数相加,结果大于机器所能表示的最大正数,称为上溢

运算结果的符号位为10表明两个负数相加,结果小于机器所能表示的最小负数,称为下溢

也就是说,两个正数相加,数值位不应向符号位同时产生进位,使得结果数的符号位和操作数的一样,为00;当运算结果的两个符号位不相同时,表明出现了溢出。


例:设x=+1100,y=+1000,求6位双符号位补码之和[x+y]补。

解:

[x]补=001100, [y]补=001000

[x+y]补=001100+001000=010100

[x+y]补=010100,其中两个符号位出现01,表示已溢出。且第1个符号位为0,上溢


例:设x=-1100,y=-1000,求6位双符号位补码之和[x+y]补。

解:

[x]补=110100, [y]补=111000

[x+y]补=110100+111000=101100

[x+y]补=101100,其中两个符号位出现10,表示已溢出。且第1个符号位为1,下溢


从上述例子中还看出,不论溢出与否,最高位始终指示正确的符号


采用双符号位补码后,任何小于1的正数,两个符号位都是0;任何大于-1的负数,两个符号位都是1

如果两个数相加后,其结果的符号位出现01或10时,表示发生溢出。因为两个绝对值小于1的数相加,其结果不会大于或等于2,所以最高位总是表示正确的符号。这也可以表示为:

当最高数据位有进位而符号位无进位时产生上溢出;当最高数据位无进位而符号位有进位时,表示下溢出。


在双符号位补码中,正常的数据中两个符号位总是相同的,所以在存储数据时不必重复存储,只是在将数据送往运算部件进行运算时才把符号位进行复制形成双符号位补码。


6.3.3 乘法运算

在计算机中,乘法运算是一种很重要的运算,有的机器由硬件乘法器直接完成乘法运算,有的机器内没有乘法器,但可以按机器作乘法运算的方法,用软件编程实现。因此,学习乘法运算方法不仅有助于乘法器的设计,也有助于乘法编程。

下面从分析笔算乘法入手,介绍机器中用到的几种乘法运算方法。

分析笔算乘法

设A=0.1101,B=0.1011,求A×B。

笔算乘法时乘积的符号由两数符号心算而得:正正得正;其数值部分的运算如下:

所以 A×B=+0.10001111

可见,这里包含着被乘数4的多次左移,以及四个位积的相加运算。

若计算机完全模仿笔算乘法步骤,将会有两大困难:

  • 其一,将四个位积一次相加,机器难以实现;
  • 其二,乘积位数增长了一倍,这将造成器材的浪费和运算时间的增加。

为此,对笔算乘法进行改进


笔算乘法的改进

将A•B = A•0.1011

   = 0.1A+0.001•A+0.0001•A

   = 0.1A+0.00•A+0.001(A+0.1A)

   = 0.1A+0.01[0•A+0.1(A+0.1A)]

   = 0.1{A+0.1[0•A+0.1(A+0.1A)]}

   = 2^-1{A+2^-1 [0•A+2^-1 (A+2^-1A)]}

   = 2^-1{A+2^-1 [0•A+2^-1 (A+2^-1(A+0))]}


由上式可见,两数相乘的过程,可视作加法和移位(乘2^-1相当于做一位右移)两种运算,这对计算机来说是非常容易实现的。


从初始值为0开始,对上式作分步运算,则:

  • 第一步:被乘数加零       A+0=0.1101+0.0000=0.1101
  • 第二步:右移一位,得新的部分积   2^-1 (A+0)=0.01101
  • 第三步:被乘数加部分积  A+2^-1(A+0)=0.1101+0.01101=1.00111
  • 第四步:右移一位,得新的部分积  2^-1 A+2^-1 (A+0)=0.100111
  • 第五步:0•A +2^-1 [A+2^-1 (A+0)] =0.100111
  • 第六步:2^-1{0•A+2^-1 [A+2^-1 (A+0)]}=0.0100111
  • 第七步:A+2^-1{0•A+2^-1 [A+2^-1 (A+0)]}=1.0001111
  • 第八步:2^-1 {A+2^-1[0•A+2^-1 (A+2^-1 (A+0))]}=0.10001111

上述运算过程可归纳为:

乘法运算可用移位和加法来实现,当两个四位数相乘,总共需做四次加法和四次移位。

由乘数的末位值确定被乘数是否与原部分积相加,然后右移一位,形成新的部分积;同时,乘数也右移一位,由次低位作新的末位,空出最高位放部分积的最低位。

每次做加法时,被乘数仅仅与原部分积的高位相加,其低位被移至乘数所空出的高位位置。


计算机很容易实现这种运算规则。用一个寄存器存放被乘数,一个寄存器存放乘积的高位,又用一个寄存器存放乘数及乘积的低位,再配上加法器及其他相应电路,就可组成乘法器。又因加法只在部分积的高位进行,故不但节省了器材,而且还缩短了运算时间。


原码一位乘法

由于原码表示与真值极为相似,只差一个符号,而乘积的符号又可通过两数符号的逻辑异或求得,因此,上述讨论的结果可以直接用于原码一位乘,只需加上符号位处理即可。

递推公式:

例:x=-0.1110 y=0.1101 求【x.y】原

即【x.y】原=0.10110110


6.3.4 除法运算

运算规则

这边直接介绍加减交替法

小数定点除法对被除数和除数有一定的约束:

0 < 被除数 <= 除数


加减交替法又称不恢复余数法,可以认为它是恢复余数法的一种改进算法。

分析原码恢复余数法得知:

  • 当余数Ri>0时,可上商“1”,再对Ri左移一位后减除数,即2Ri-y*。
  • 当余数Ri>0时,可上商“0”,然后再做Ri+y*,即完成恢复余数的运算,再做2(Ri+y*)-y*,也即2Ri+y*。


可见,原码恢复余数法可归纳为:

  • 当余数Ri>0时,商上“1”,做2Ri-y*的运算;
  • 当余数Ri<0时,商上“0”,做2Ri+y*的运算。

这里已看不出余数的恢复问题了,而只是做加y*或减y*,因此,一般把它叫做加减交替法或不恢复余数法。


例:已知:x=-0.1011,y=-0.1101,求:[x÷ y]原

解:

[x]原=1.1011, x*=0.1011

[y]原=0.1101,y*=0.1101,[-y*]补=1.0011

商值的求解过程如下表所示:

商的符号位为

所以

分析此例可见,n位小数的除法共上商n+1次,第一次商用来判断是否溢出。

倘若比例因子选择恰当,除数结果不溢出,则第一次商肯定是0。如果省去这位商,只需上商n次即可,此时除法运算一开始应将被除数左移一位减去除数,然后再根据余数上商。


大概过程:

  • 先+【-y*】补,如果余数为正数代表溢出 仔细思考亿下 被除数+(-除数)>0;这边的被除数、除数均>0;即被除数>除数;这和小数定点除法规则违背,即溢出
  • 余数为负数,上商0,左移1位后 +【y*】补
  • 余数为正数,上商1,左移1位后 +【-y*】补


原码交替法所需的硬件配置

原码加减交替除法流程控制


除了用原码计算,还可以用补码进行计算,计算出来的是补码,我们只需掌握原码计算法,把得到的结果转为补码就行了,这里就不再细说了



6.4 浮点加减运算

首先给出浮点数加减法运算步骤:

  1. 对阶,使得两数的阶数位置对齐
  2. 尾数求和,将对阶后的尾数按定点加减运算规则求和/差
  3. 规格化,将求和/差后的尾数进行规格化
  4. 舍入,为提高精度,要考虑尾数右移时丢失的数值位
  5. 溢出判断,即判断结果是否溢出


6.4.1 对阶

类比平常我们用到的带阶数的加减法,我们常常会把两个数的阶数转化为一致,再针对尾数进行加减法运算,计算机中也是一样。

对以上两个浮点数,我们进行对阶,首先求他们的阶差

在对阶的过程中有两种方式,大阶向小阶看齐,小阶向大阶看齐。

但是在计算机中,因为存储空间的字长限制,如果我们使用大阶向小阶看齐,需要把数字向左移,可能把数字的最高位给移除掉,从而使整个数据出现错误;

如果我们使用小阶向大阶看齐,需要把数字向右移,有可能丢失数据的精度,但不会使整个数据出现错误;

因此我们对阶的原则是:小阶向大阶看齐

求 X + Y

可以看到这边我们也用到了上文提到的双符号

6.4.2 规格化

规格化的判断

判断一个数是否是规格化数,计算机中可以通过异或电路,比较一个数补码的符号位跟第一位数是否相同来实现。


概括一下:

对于双符号位的补码规格化形式位:00.1xxxxxxxx;11.0xxxxxxxx


左规

尾数左移一位(大小变为原来的两倍),阶码减 1 ,直到数符和第一数位不同为止

计算 X + Y的结果为: [ x + y ] 补 = 00, 11; 11. 1001

结果为非规格化数,左规后

注意这边0.1110是原码

右规

当 尾数溢出 ( >1 )时,需 右规, 即尾数出现 01. ××…×或 10. ××…×时, 尾数右移一位,阶码加 1


总结:

当尾数出现00.0xxxxxxx、11.1xxxxxxxxx 需要左规

当尾数出现01.xxxxxxxx、10.xxxxxxxxxx 需要右规

无论左规还是右规对尾数进行移位是都是逻辑移位(即符号位也参与移位)

例:

求x+y(除阶符、数符外,阶码取3位,尾数取6位)

通过双符号位,我们可以知道尾数相加后,尾数溢出(且为上溢),因此我们需要进行右规

注意这边0.100101是原码

6.4.3 舍入

执行右规或者对阶时,有可能会在尾数低位上增加一些值,最后需要把它们移掉。(进行尾数加减时不要把对阶过程在尾数低位上增加的值去掉,不是不去掉,而是在舍入这一步去掉。)比如说,原来参与运算的两个数(加数和被加数)算上符号位一共有6个数,通过一系列操作后运算结果变成了8个数,这时需要把第7和8位的数去掉。如果直接去掉,会使精度受影响,通常有下边两个方法:

0舍1入法

比如:X = 00.11010111,假设原本加数和被加数算上符号位一共有6个数,结果X是10个数,那么要去掉后四个数(0111)。由于0111首位是0(即要去掉的数的最高位为0),这种情况下,直接去掉这四个数就可以。所以最后结果为 X = 00.1101

结果 X = 00.11001001,这时要去掉的数为1001四个数,由于这四个数的首位为1(即要去掉的数的最高位为1),这种情况下,直接去掉这四个数,再在去掉这四个数的新尾数的末尾加1。如果+1后又出现了溢出(即符号位为:01或10),继续进行右规操作。所以最后结果为 X = 00.1101。


置1法

这个比较简单,去掉多余的尾数,然后保证去掉这四个数的新尾数的最后一位为1(即是1不用管,是0改成1)即可。比如 Z=00.11000111,置1法之后的结果为Z=00.11001。

6.4.4 溢出判断

阶码溢出在规格化和右移的过程中都有可能发生,

  • 若阶码不溢出,加减运算正常结束(即判断浮点数是否溢出,不需要判断尾数是否溢出,直接判断阶码是否溢出即可)。
  • 若阶码下溢,置运算结果为机器0(通常阶码和尾数全置0)。
  • 若上溢,置溢出标致。

例如:

设机器数为补码,尾数为 规格化形式, 并假 设阶符取 2 位,阶码的数值部分取 7 位,数符取 2 位,尾数取 n 位,则该 补码 在数轴上的表示为


6.5 算术逻辑单元

6.5.1 ALU电路

Arithmetic Logic Unit

电路图

ALU是一个组合逻辑电路,所谓组合逻辑电路指电路没有记忆功能,所以Ai,Bi 存储在寄存器中


6.5.2 快速进位链

1. 并行加法器

2. 串行进位链

3. 并行进位链

要求n位加法器的进位同时产生,分为单重分组跳跃进位链和双重分组体跳跃进位链

单重分组跳跃进位链

n位全加器分为若干小组,小组中的进位同时产生,小组和小组之间采用串行进位

以 n = 16 为例


双重分组跳跃进位链

n位全加器分为若干大组,大组中又包含若干小组。每个大组中小组的最高位进位同时产生。大组与大组之间采用串行进位

以 n = 32 为例

分为2大组,每个大组有4个小组,每个小组有4位数据

第2大组进位图(第1大组进位图和这个一样)

C15 会传到第1大组中


完整图

举报
评论 0