2.2.2 使用二进制数表示负整数
一组二进制序列如何能够表示正整数、零和负整数呢?人们想了一个非常聪明的方法来解决它,即将给定二进制序列的最高有效位(Most Significant bit,MSB)作为符号位。例如,对于一个8位字长的二进制序列,第7位为符号位(最低位的索引号为0);对于一个16位字长的二进制序列,第15位为符号位(最低位的索引号为0);对于一个32位字长的二进制序列,第31位为符号位(最低位的索引号为0)。
当符号位为0时,规定为正整数;当符号位为1时,规定为负整数。例如:对于正整数35来说,用8位字长二进制序列表示为“00100011”;对于负整数-35来说,用8位字长二进制序列表示为“10100011”,如表2.7所示。
表2.7 有符号数的二进制表示(-35)
从表2.7中可知,这种用来表示有符号整数的方法称为幅度-符号表示法,也就是传统教科书上所介绍的二进制的“原码”。与传统教科书使用“原码”这一术语相比,使用幅度-符号表示法更能说明其真实的物理含义。
幅度-符号法是否能够作为有符号整数的表示方法呢?其实问题没有这么简单。这是因为幅度-符号法在表示有符号数方面有很大的局限性。比如,对于+0和-0这两个整数,虽然它们的值(幅度)都为零,但是使用幅度-符号法表示零的时候却出现完全不同的结果,即+0表示为“00000000”、-0表示为“10000000”。
进一步地,从图2.1(a)可知,将8位字长的二进制数从00000000开始,以顺时针方向递增,并均匀排列在圆上,即得到从“00000000”到“11111111”的排列形式。根据幅度-符号表示法,二进制序列“00000000”表示为+0,二进制序列“10000000”表示为-0,二进制序列“01111111”表示为+127,二进制序列“11111111”表示为-127。
图2.1 有符号整数的不同表示方法
这样,在二进制序列+0的左侧是最小的负整数-127,而在二进制序列+0的右侧是最小的正整数+1。很明显,在正零的两侧出现了不连续区域(突变),即从-127跳变到0,然后再变化到+1。这与在X轴上表示正整数、零和负整数的方法出现了冲突,我们知道在X轴上正常应该是-1、0和+1的变化。
所以,采用幅度-符号或“原码”表示有符号整数是有问题的。那应该如何解决这个问题呢?根据数字逻辑电路上所介绍的使用全加器执行减法运算的规则可知,如果将减数所对应的二进制数先全部按位取反然后再加一,就可以将减法运算转化为加法运算,此时就可以使用加法器实现减法运算。这种变换方法在计算机里称为二进制序列的“补码”运算。那如何使用二进制序列的补码形式来表示有符号数呢?下面通过几个例子来说明得到负整数所对应二进制补码的方法。
注:对于有符号数中的正整数来说,其补码与原码(幅度-符号表示法)完全相同。
1)求取-1的二进制补码形式(8位字长)
首先得到-1所对应的+1的二进制原码(无符号正整数不考虑符号位)表示,即“00000001”;然后将该原码序列全部按位取反,“0”变成“1”,“1”变成“0”,得到所对应的反码为“11111110”;最后将所得到的反码结果加1,即得到“11111111”,因此-1的二进制补码序列为“11111111”。
2)求取-0的二进制补码形式(8位字长)
首先得到-0所对应的+0的二进制原码(无符号正整数不考虑符号位)表示,即“00000000”;然后将该原码序列全部按位取反,“0”变成“1”,“1”变成“0”,得到所对应的反码为“11111111”;最后将所得到的反码结果加1,即得到“100000000”,由于是8位字长,舍弃第9位的“1”,最终得到-0的二进制补码序列为“00000000”。
也就是说,+0的二进制补码序列和-0的二进制补码序列完全相同,这样就解决了在使用幅度-符号表示法时出现对0的二义性的描述问题。
3)求取-128的二进制补码形式(8位字长)
首先得到-128所对应的+128的二进制原码(无符号正整数不考虑符号位)表示,即“1000000”;然后将该原码序列全部按位取反,“0”变成“1”,“1”变成“0”,得到所对应的反码为“01111111”;最后将所得到的反码结果加1,即得到“10000000”,最终得到-128的二进制补码序列为“10000000”。
规则总结:得到负整数所对应正整数原码(不考虑符号位)表示,然后将所得到的原码全部按位取反得到对应的反码,最后在反码上加1,这样就得到了负整数所对应的二进制补码表示形式。
是不是使用二进制补码形式就解决了幅度-符号表示法的缺陷呢?再观察图2.1(b),二进制数的排列规则和图2.1(a)完全相同,只是二进制序列所对应的有符号整数的含义发生了变化。首先,从图中可知,整数中的零只有一个对应的二进制序列“00000000”。其次,在零值左侧,二进制补码序列“11111111”所对应的是-1;在零值右侧,二进制补码序列“00000001”所对应的是+1,因此从-1到0再到1的变化是连续的,而不是突变的。最大正整数+127左侧的二进制补码序列“10000000”,所对应的是-128。这与传统上在X轴上表示从-128到+127是完全相同的。因此,使用二进制补码的形式来表示有符号的整数(包括正整数、零和负整数)。
从图2.1(b)中可知,对于8位字长的二进制序列来说,其可表示的整数范围为-128~+127,用公式表示为-28-1~28-1-1。
推而广之,对于n位字长的二进制序列,其可以表示的整数范围为-2n-1~2n-1-1。比如,对于16位字长的二进制序列,其可以表示的整数范围是-215~215-1,即-32768~+32767;对于32位字长的二进制序列,其可以表示的整数范围是-2147483648~+2147483647。
前面给出的是通过对原码取反加1得到补码的方法,下面介绍通过比较法得到二进制补码的方法。以十进制有符号负整数-97转换为所对应的8位二进制补码序列为例,如表2.8所示。
表2.8 十进制有符号负整数-97的二进制补码比较法实现过程
(1)得到需要转换负数的最小权值,该权值为负数,以-2i表示(i为所对应符号位的位置),使其满足:
-2i≤需要转换的负数
并且,-2i与所要转换的负整数有最小的距离,保证绝对差值最小。
(2)取比该权值绝对值2i小的权值,以从2i-1到20的顺序表示。比较过程描述如下。
① 需要转换的负数加上2i,得到了正整数,该正整数作为下一次比较的基准。
② 后面的比较过程与前面介绍的正整数比较方法一致。
根据表2.8给出的比较过程,得到负整数-97所对应的二进制补码序列为“10011111”。
思考与练习2-7:计算下面负整数所对应的补码,并给出详细的计算过程。
(1)-127的补码是____________________(使用8位二进制数表示)。
(2)-32767的补码是____________________(使用16位二进制数表示)。
(3)-2的补码是________________________(使用16位二进制数表示)。
思考与练习2-8:对于一个8位的二进制序列“10100101”,当该序列表示无符号数时,所对应的十进制正整数为________________;当该序列表示有符号数时,所对应的十进制整数为____________。
这里需要强调,当使用补码表示整数(包括负整数、零和正整数)时,正整数的二进制原码和补码完全相同,只是负整数需要使用二进制补码表示而已。