3.1.2 有符号数
二进制数的每一位有0、1两个数值,而数的符号有“正”“负”两种状态,那么可以用“0”表示“正”,“1”表示“负”。当指定一个数为有符号类型时,用二进制数的最高位表示数的“正”“负”。
无符号数中所有的位都用于表示数的大小,有符号数中用最高位表示正负,所以当为正值时,能表示的数的最大值就会变小。例如:
无符号数:11111111,十进制值为:255
有符号数:01111111,十进制值为:127
同样是一个字节,无符号数的最大值为255,而有符号数的最大值为127,原因是有符号数中的最高位被用来表示符号了,用来表示数值的位变为了7位,并且最高位的位权为27,所以仅仅少了一位,最大值减半。
不过有符号数可以表示负数。因此,虽然最大值变小了,但是在负值的方向得到了扩展。下面同样用一个字节的数作对比:
无符号数取值范围为:0~255
有符号数取值范围为:-128~127
无符号数的最小值是0,而有符号数的最小值是-128,二者能表示的数值个数都是256个,只不过前者表示的是0~255这256个数,后者表示的是-128~127这256个数。那么,
如何将这256个不同的符号串对应为具体的实际值呢?下面介绍几种常见的编码方式。
(1)原码
计算机中用二进制数的最高位表示有符号数的符号,那么,
当x=+0010000时,就可以写为0,0010000,对应的十进制数为16;
当x=-0001000时,就可以写为1,0001000,对应的十进制数为-8。(用逗号把符号位和数值分开)
有符号数的这种表示方式,称为原码。如果把原码的长度确定为n,那么数x的原码定义如下:
现在考虑n=8,x=0时,x的原码该如何表示。当x=0时,理论上,x的符号可以为“+”,也可以为“-”,那么
[-0]原=0,000 0000
[+0]原=1,000 0000
显然[+0]原不等于[-0]原,即0的原码表示存在两种情况。
下面再考虑另外一个问题——使用原码进行加减运算。假如有两个数16和-8相加,16的8位二进制原码表示是(0 ,0010000),-8的8位二进制原码表示是(1,0001000),那么这两个二进制数的加法写为
可以看到,如果按照正常的二进制加法规则计算,就会得到10011000,转换成十进制数就是-24,这显然是错误的。也就是说加法规则不适用于正数和负数相加(也不适用于负数和负数相加)。如果要用原码表示法,则在设计加法器时就考虑两种不同的规则,即没有负数的加法规则和有负数的加法规则。这样的话电路设计就会比较复杂。
(2)补码
既然用原码表示有符号数时,“0”有两种情况,而且有负数的加法也不能使用一般的加法规则,那么就需要考虑使用别的方式来表示二进制有符号数。
先考虑一个日常生活中的例子。时钟的时针转一圈有12小时,假如现在时钟指示4点,要让时针指示2点,可以按顺时针方向把时针转10格,也可以把时针按逆时针方向转2格。假设顺时针方向转动为正,逆时针方向转动为负,则
4-2=2
4+10=14
虽然两种方法的结果表面上看起来不一样,但2点和14点均指向同一个数2,实际上是同一个结果。这样看来-2和10对时钟来说其作用是一样的。在数学上12叫做模,对模12而言,-2和10互为补数,记作
-2≡10(mod12)
同理,有
-3≡9(mod12)
-4≡8(mod12)
由此可见,可以找到一个与负数等价的数(这里称其为该负数的补数)来代替该负数进行运算。回到前面两个二进制原码相加的例子,由于二进制数的总位数为8,可以表示256个不同的数,则8位二进制数的模为256,写为二进制为100000000。于是可以算出,对模256而言,-8的补数为248,二进制数为11111000。那么对于两个十进制数16和-8相加的计算,就可以把原码中表示的00010000+10001000写为00010000+11111000,得到的结果为100001000,由于规定二进制数的位数为8,则计算结果的最高位溢出,实际得到的结果为00001000,转换为十进制数是8,这样就得到了正确的结果。在计算机中,把补数的二进制形式称为补码。
实际上,在计算机中每个数都是用补码来表示的,包括正数。正数的补码就是正数本身,负数的补码等于模加上这个负数。对于一个n位的二进制数x,它的模为2n,那么数x的补码的定义如下:
通常我们并不用这种方法计算负数的补码,而是用另一种更简单的方法。例如,求-8的补码,如果按照上面的定义来求,则
[-8]补=1 0000 0000-0000 1000=1111 1000
进一步,我们可以把100000000写为11111111+1,则上式可写为
[-8]补=1111 1111-0000 1000+1 =1111 0111+1
可以发现11110111正好是00001000各位取反的结果,于是便有了一种求负数补码的简单方法,即把负数绝对值的二进制表示按位取反,然后加1,简称“取反加1”。这样就把减法运算变为取反和加法运算,也更容易用硬件实现。
(3)反码
反码通常用来作为由原码求补码或者补码求原码的过渡。正数的反码等于其本身,负数的反码等于把负数的绝对值的二进制表示按位取反。定义如下: