计算机中,除法运算和乘法运算一样,是非常常用的一种运算。同样,除法运算在计算机中的实现也分为符号部分和数值部分两部分。
(1)符号位。符号位的确定和乘法运算的规则一致,除法运算的符号位无法通过转换补码,加入到除法运算中,必须单独进行处理。根据除法运算的规则:被除数和除数之间,符号位相同则为正,符号位不同则为负。设被除数和除数分别为X和Y,Xf和Yf分别代表X和Y的符号位,除法运算结果为Z,Zf代表Z的符号位。如下表所示。
Xf | Yf | Zf |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
根据真值表,可得除法运算符号位的逻辑表达式为:
Zf=Xf异或Yf
(2)恢复余数法实现数值部分除法。数值部分除法在计算机中的实现也是从除法运算的笔算演变过来的。我们不考虑符号位,以两个正数的除法为例,做出一个除法运算笔算例子的完整过程。
【例】设二进制小数X=0.1001,Y=0.1101,计算X/Y
X/Y的竖式如下:
0. 1 0 1 1
0.1101 / __________________
0. 1 0 0 1 0
0. 0 1 1 0 1
—————————————————
0. 0 0 1 0 1 0 0
0. 0 0 0 1 1 0 1
—————————————————
0. 0 0 0 0 1 1 1 0
0. 0 0 0 0 1 1 0 1
—————————————————
0. 0 0 0 0 0 0 0 1
结果为:X/Y的商为0.1011,余数为0.00000001
计算机中的定点数的除法运算,商的位数一般与被除数和除数的位数相等。观察运算过程发现,除法运算笔算每次都上商,都是通过心算比较余数和除数的大小关系,如果余数大于除数,上1,然后做减法得出新的余数,新的余数低位补0;如果余数小于除数,则上0,余数低位补0得出新的余数。
所以,除法运算的笔算每次上商是1还是0,关键是看余数和除数之间的大小关系比较。笔算中我们都是通过心算进行大小比较,但是,机器没有所谓“心算”,只能在余数和除数之间做减法操作,查看结果正负来判断大小关系。如果余数减去除数的结果为正,说明应该上商为1,而将减法的结果低位补0,就得到新的余数;而如果余数减去除数的结果为负,说明应该上商0,减法的结果无意义,恢复余数法的做法是,将减法结果加上除数,还原减法操作前的余数,然后再将余数低位补0,得到新的余数。
从上面除法运算笔算 例子的竖式中可以看出,余数和除数之间的减法操作,在计算机中实际上使用的是加法器,将减法转换为补码加运算的方法实现的。设余数为A,除数为B,则:
[A-B]补=[A+(-B)]补=[A]补+[-B]补
观察笔算除法竖式发现,每次上商后,除数都需要右移一位来与新的余数对齐,机器字长为5的除法运算需要加法器的位数至少为9,。计算机中,我们可以对笔算算法稍作改变,除数右移一位的操作可以用余数左移一位来代替。这样就不需要在余数和新除数前加连续的0.但左移后的余数已经不是真正的余数,只有再将余数重新右移才能得到真正的余数。
笔算求商时,商的结果是从高位到低位逐位算出来的。在计算机的实现中,计算出每一位的商以后,并不是直接把结果写到寄存器相应的位中,而是从高位开始,将每一位的商写到寄存器的最低位,然后左移一位,等待下一位商写到寄存器的最低位,再左移,再求下一位商……如此循环直到最低位写到寄存器中。
此外,定点小数的除法,如果被除数的绝对值大于或等于除数的绝对值,那么很明显,除法结果的绝对值会大于或等于1,即商成为一个非纯小数。无法再用定点小数表示。并且,除法运算应该避免被除数和除数为0.所以,设被除数为X,除数为Y,X*和Y*分别为X和Y的绝对值。定点小数的除法必须满足下面的条件:
0<|被除数|<|除数|
【例】设二进制纯小数X=-0.1001,Y=0.1101,请使用恢复余数法,计算并列出执行X/Y的操作过程。
求X和Y的原码,得
[X]原=1.1001,[Y]原=0.1101
首先判断符号位:
Zf=Xf异或Yf=1异或0=1
求x和y的绝对值,得
X*=0.1001,Y*=0.1101
求X*和Y*的原码,得
[X*]原=0.1001,[Y*]原=0.1101
求X*、Y*和-Y*的补码,得
[X*]补=0.1001,[Y*]补=0.1101,[-Y*]补=1.0011
余数恢复法执行过程如下:
①余数R=X*.商为0.0000.
②[R]补=[R-Y*]补=[R]补+[-Y*]补=0.1001+1.0011=1.1100,结果为负数。
③商的末尾置0,为0.0000,左移一位,还是0.0000;余数还原:[R]补+[Y*]补=1.1100+0.1101=0.1001;余数左移一位,末位补0,得[R]补=1.0010.
④[R]补=[R-Y*]补=[R]补+[-Y*]补=1.0010+1.0011=0.0101,结果为正数。
⑤商的末位置1,为0.0001,左移一位,得商为0.0010;余数左移一位,末位补零,得[R]补=0.1010
⑥[R]补=[R-Y*]补=[R]补+[-Y*]补=0.1010+1.0011=1.1101,结果为负数。
⑦商的末尾置0,为0.0010,左移一位,得商为0.0100;余数还原:[R]补=[R]补+[Y*]补=1.1101+0.1101=0.1010;余数左移一位,末位补零,得[R]补=1.0100
⑧[R]补=[R-Y*]补=[R]补+[-Y*]补=1.0100+1.0011=0.0111,结果为正数。
⑨商的末尾置1,为0.0101,左移一位,得商为0.1010;余数左移一位,末尾补零,得[R]补=0.1110
⑩[R]补=[R-Y*]补=[R]补+[-Y*]补=0.1110+1.0011=0.0001,结果为正数。
①①商的末位置1,为0.1011.
数值运算结果和符号结果结合,得[X/Y]原=1.1011
总结:首先根据计算位数设商为0.0000(本题的小数点后的精度为4,超出的均为溢出丢弃),然后根据[R]补=[R-Y*]补=[R]补+[-Y*]补的运算结果正负,来确定商的末尾置0还是置1(负数置0,正数置1),并且在每次计算后,计算结果都要左移一位,末尾补零,得到新的余数,然后再用这个新的余数进行计算(同样的公式)。记得这一点差别,商的末位无论置0还是置1,也需要左移,但是在下一步进行置0或是置1是在未移动的结果上进行置0或置1(不能用移动后的商)。同时,如果余数计算的结果为负,那就要恢复余数,也就是用余数再加上除数,然后再进行下面的操作。对于循环几次,取决于除数的位数,本题中除数小数点位数为四位,所以要移动四次,得到最后的结果便是结果(这时无符号位),最后我们再加上一开始判断的符号位便是最终结果。
(3)加减交替法实现数值部分除法。加减交替法也称不恢复余数法,是对恢复余数法的一种改进算法。恢复算法每次用余数减去除数,都是为了观察余数和除数的大小关系,如果减法结果为负数,说明余数小于除数,那么就必须将结果再加除数来还原余数。反复的加减操作很大程度上影响了恢复余数法的运算效率。
再次分析原码恢复余数法发现,余数减去除数,设结果为r:
如果R>0,上商1,R 左移一位,低位补0,就得到新的余数。下一步新的余数再减除数,确定下一位商的结果。也就是说,如果R>0,确定下一位的商和r值的运算为:
R=2R-Y*
如果R<0,上商0,然后需要恢复余数,即R+Y*,将R+Y*的结果再左移一位,低位补0,就得到新的余数。下一步新的余数再减去除数,确定下一位商的结果。也就是说,如果R<0,确定了下一位商和R值的运算为:
R=2(R+Y*)-Y* 即R=2R+Y*
如此一来,当每次余数减除数的结果R为正或者负时,我们只需要根据正负上商,并按照不同公式计算新R的值来确定下一位商即可。不需要在进行当R为负数时恢复余数这样繁琐的操作了。
【例】设二进制纯小数X=-0.1001,Y=0.1101,请使用加减交替法,计算并列出执行X/Y的操作过程。
求X和Y的原码,得
[X]原=1.1001,[Y]原=0.1101
首先判断符号位:
Zf=Xf异或Yf=1异或0=1
求X和Y的绝对值,得X*=0.1001,Y*=0.1101
求X*和Y*的原码,得
[X*]原=0.1001,[Y*]原=0.1101
求X*、Y*、-Y*的补码,得
[X*]补=0.1001,[Y*]补=0.1101,[-Y*]补=1.0011
加减交替法的执行过程如下:
①设余数R=X*=0.1001,商为0.0000.
②[R]补=[R-Y*]补=[R]补+[-Y*]补=0.1001+1.0011=1.1100,结果为负数。
③商的末位置0,为0.0000,左移一位,还是0.0000
④[R]补=[2R+Y*]补=2[R]补+[Y*]补=1.1000+0.1101=0.0101,结果为正数。
⑤商的末位置1,为0.0001,左移一位,得商为0.0010。
⑥[R]补=[2R-Y*]补=2[R]补+[-Y*]补=0.1010+1.0011=1.1101,结果为负数。
⑦商的末位置0,为0.0010,左移一位,得商为0.0100.
⑧[R]补=[2R+Y*]补=[2R]补+[Y*]补=1.1010+0.1101=0.0111,结果为正数。
⑨商的末位置1,为0.101,左移一位,得商为0.1010.
⑩[R]补=[2R-Y*]补=[2R]补+[-Y*]补=0.1110+1.0011=0.0001,结果为正数。
①①商的末位置1,得商为0.1011
数值运算结果和符号结果结合,得[X/Y]原=1.1011
总结:加减交替法采用了公式计算,前面和不恢复余数法一样,计算符号位,设余数为被除数,商为0,[R]补=[R-Y*]补=[R]补+[-Y*]补计算,而下面需要根据结果的正负来使用公式,如果结果为负数,则用R=2R+Y*计算出余数,然后再进行判断。如果结果为正数,则用R=2R-Y*计算,然后判断正负。循环的次数和不恢复余数法一样,由除数决定。最后循环结束再结合符号位就是最终的结果。
浮点数的加减运算
浮点数与定点数相比,所表示的范围更宽,有效精度更高,更加适合于科学计算。但浮点数的格式比定点数要复杂,硬件电路更复杂,实现的成本更高。一些微处理器自身不带有浮点运算功能,但另外配有协处理器,专门用于浮点数的四则运算。
我们在前面讨论了浮点数在机器中的表示方法。阶码E采用补码或移码的方式表示;尾数F采用原码或补码方式表示。设有两浮点数X和Y进行加减运算时,必须按以下几步执行。
1.对阶
使X和Y的小数点位置对齐。才可以进行加减操作。
由于阶码不同,X和Y的尾数的对应位所代表的权值是不同的。加减操作前,必须将X和Y的小数点位置对齐,也就是使X和Y的价码相等。对阶的原则是阶码小的数进行调整,打破浮点数规格化的要求,使两个数的价码相等。例如:
设二进制浮点数X=101.1和Y=1.011,如果要执行X+Y。首先看X和Y在计算机中用N=2^E*F的形式表示,使用规格化形式,为:
X=2^3 * 0.101100
Y=2^1 * 0.101100
X和Y的价码分别为3和1,尾数不能直接相加。按照阶码小的数向阶码大的数对齐原则,调整后的Y的表示为:
Y=2^3 * 0.001011
2.尾数加减
将对阶后的两尾数按定点加减运算规则进行操作,尾数的加减运算一般使用变形补码的加减运算方式来实现。
继续上面的例子,X和Y对阶完成后,尾数就可以进行加运算:
[0.101100]补`=00.101100
[0.001011]补`=00.001011
[0.101100+0.001011]补`=[0.101100]补+[0.001011]补`=00.101100+00.001011=00.110111
3.规格化
为增加有效数字的位数,提高运算精度,必须将求和(差)后的位数规格化。规格化又分为左规和右规两种:
(1)左规。当尾数的变形补码加减运算结果出现00.0***或11.1*****时,说明加减操作无溢出,并且,最高数值位表明,尾数不符合规格化要求,需左规。左规时尾数左移一位,阶码减一,直到符合补码规格化表达式为止,即结果为00.1********或11.0****
(2)右规。当尾数出现01.********或10.**********时,表示尾数的变形补码加减运算结果溢出。在这定点加减运算中是不允许的,但在浮点运算中这不算溢出,可通过右规处理后继续使用。右规时尾数右移一位,阶码加一。
继续上面的例子,执行完尾数加减操作后,X+Y的结果为:
Z=2^3*0.110111
结果符合规格化要求,所以X+Y的结果即为上式。用实数方式表示为:X+Y=(110.111)2进制
【例】两浮点数X=2^+010 * 0.110100,Y=2^+100 * (-0.101010),求X+Y
阶码取三位,尾数取6位(均不包括符号位),设阶码和尾数均采用补码表示方式,机器表示的形式分别为:
[X]补=0010 0110100
[Y]补=0100 1010110
第一步,对阶:先求阶差(两阶码的补码相减),减去正数补码0100就是加负数补码1100,使用变形补码方式执行:
00 010+11 100=11 110
结果的真值为-2,即X的阶码比Y的阶码小2.[X]补的阶码增大成0100,尾数右移两位,即
[X]补=0100 0001101
第二步:尾数以变形补码的形式相加:
00.001101+11.010110=11.100011
相加结果为:0100 1100011
第三步:规格化:
最高有效位与符号位相同,需要左规,尾数左移一位,阶码减1,结果为:
[X+Y]补=0011 1000110,即
X+Y=2^+011 * (-0.111010) 【补码的补码即为原码】
4.舍入
在对阶和右规的过程中,可能会将尾数的低位丢失,引起误差,影响了精度,为此可以用舍入法来提高尾数的精度。常用的舍入法有三种:截去法、“恒置1”法和“0舍1入”法。
截去法最简单,不用考虑 右移操作导致丢失的数据,直接丢弃。
“恒置1”法也不考虑右移操作导致被丢掉的数据,而是直接将右移操作后的末尾恒置"1".从统计学角度看,“恒置1”法的平均误差为0,与截去法相比,有更高的可能性使结果更加准确。
“0舍1入”法类似于十进制运算中的“四舍五入”法,即在尾数右移时,如果被移去的数值的最高位为0,则直接舍去;如果被移去的数值最高位为1,则在新尾数的末位加1.这种方法可以保证最大误差是最低误差上的-1/2到1/2之间,正误差可以和负误差抵消。是比较理想的方法,但实现起来比较复杂。并且有可能使尾数又溢出,如果溢出则再做一次右规。
【例】两浮点数X=2^+10 * 0.1101,Y=2^+01 * 0.1011,求X+Y,舍入用“0舍1入法”。
阶码取三位,尾数取四位(均不包括符号位),设阶码和尾数均采用补码表示方式,机器表示的形式分别为:
[X]补=0010 01101
[Y]补=0001 01011
第一步,对阶,我们换一种方式,通过观察,我们发现Y的阶码比X的阶码小1,所以把Y的阶码编程0010,需要尾数右移,即01011右移一位,为00101,题目要求用0舍1入法,我们舍去的为1,所以尾数要加1,所以最终的位数为00110.[Y]补=0010 00110
第二步,尾数以变形补码形式相加
00.1101+00.0110=01.0011
第三步,规格化
因位数符号位为01,需要右规(尾数右移一位,阶码加1),右移后的位数结果为:01001.根据“0舍1入”法可知,尾数被移去一位,该位为1,所以尾数右移一位后末位要加1,即01010,得
[X+Y]补=0011 01010
浮点数的乘法和除法运算
按照数学运算公式,我们知道,两个浮点数相乘,乘积的阶码等于两个乘数的阶码之和,乘积的尾数等于两个乘数的尾数的相乘;两个浮点数相除,商的阶码等于被除数的阶码减去除数的阶码,商的尾数等于被除数的尾数除以除数的尾数。设浮点数X和Y分别为:
X=2^EX * Fx
Y=2^EX * Fy
则
X * Y=2^(EX+EY) * (Fx * Fy)
X / Y=2^(EX-EY) * (Fx/Fy)
一般乘除法运算之前,首先会检测能否简化操作。比如,检测乘法(除法)是否有乘数(被除数)为0,如果有乘数(被除数)为0,那么乘积(商)必为0.
从数学运算公式就可以看出,浮点数的乘法和除法主要包括两组定点运算,分别为定点整数的阶码加减和定点小数的尾数乘除运算。尤其是除法运算中的尾数除法运算,要注意被除数尾数的绝对值是否小于除数尾数的绝对值,以确保商的尾数为小数。如果不是,则需要调整阶码,将被除数尾数右移一位,价码加1,然后再执行除法运算。
最后,乘除运算过程也要考虑规格化和舍入的问题,以及溢出问题,尤其是阶码加减运算比较产生溢出。