一、计算机的负数表示

二、1的补码One\’s complement——反码

三、2的补码Two\’s complement——补码

   

一、计算机的负数表示

数据在计算机中由一个一个的0,1比特表示,所以在表示负数的时候,不能直接添加符号\’-\’来表示这是个负数,必须采用一些规范或者约定来区分正数和负数。

有四种比较有名的表示负数的方法:原码(sign-and-magnitude)1的补码(one\’s complement)2的补码(two\’s complement),以及excess-k(即Offset binary)

   

二、1的补码One\’s complement——反码

1、特性

1的补码比较简单,它在表示负数的时候简单把该负数的绝对值(即相应的正数)的二进制位全部反转。比如1用0000 0001表示,那么-1就用1111 1110表示。

比较有意思的是由于+0用0000 0000表示,反转后1111 1111就表示-0。所以在1的补码里有两个零。

   

Binary value

Ones\’ complement interpretation

Unsigned interpretation

00000000

+0

0

00000001

1

1

01111101

125

125

01111110

126

126

01111111

127

127

10000000

127

128

10000001

126

129

10000010

125

130

11111101

2

253

11111110

1

254

11111111

0

255

   

From <http://en.wikipedia.org/wiki/Signed_number_representations#Ones.27_complement>

   

以上表举例来看:(-1)+1 = 0,对应的1的补码加法

0000 0001 ——-1

  +1111 1110 ——1

=========

1111 1111——-0

   

由于负数都是由相应的正数二进制位全反转得到的,所以这里有个特性就是把它们相加的话会得到一个二进制位为全1的数,在1的补码的表示方法中,全1表示-0。这也是为什么这种表示方法叫做”1的补码”。1的补码在IP,TCP,UDP,ICMP等协议头中应用于计算校验和。

还有一点很有意思的是,在1的补码表示的数中,所有的正数最高比特都是0,而所有的负数最高比特都是1。而且它的正最大值是127,没有向上加过去表示128。在我们平常来看,最高位表示符号位0代表正,1代表负是极为正常的事。但是在1的补码里,没有设置符号位,之所以造成这样的”巧合”,存在着一定的必然性。我们假设它能表示到128,即1000 0000,那么-128就表示成0111 1111,可是0111 1111已经用来表示127了,这就造成了冲突。其实问题很简单,我们可以把最高位的0,1看作是前缀码,用这位前缀码保证对正数的反转操作不存在冲突。这也是为什么正数最大只能表示到127。

更有意思的是,国内将1的补码翻译成反码,其求负数的反码时,”保留符号位,将其余的比特进行反转”。但是这样就不能解释为什么有-0的存在。更不能解释在IP,TCP,UDP,ICMP协议头里求校验和时所谓的”反码求和”是怎么回事。

想象有一根数轴,两端分别向正无穷和负无穷延伸,从中间0的位置一刀劈下去(0有两个),分成非负和非正的两半。

   

这根轴看起来很对称。

如果我们从+0开始数数,即从0000 0000开始不断加1(0000 0001,0000 0002,0000 0003…),我们会发现我们不停在循环(\’0\’-\’127\’,\’-127\’-\’0\’)。原因是当我们数到127(0111 1111)时,再加1变成-127(1000 0000)。这个时候看起来是这样的:

   

   

   

2、1的补码的加法

1的补码的加法跟其它的加法是一样的,只是它有个循环进位(end-around carry)概念。它要求把最后结果的进位加回到最后的结果中去。

比如计算(-1)+2:

1111 1110

   +0000 0010

=========

 (1)0000 0000

           + 1 进位

=========

0000 0001

   

为什么要这样呢?

我们考虑在①+127处,我们加1变成-127,在-127处我们再减1,同样会出错。在应用的时候我们要注意这种超出范围的错误。②处,由于我们有两个零,在做加法时,我们要加回一个0去。比如还是计算(-1)+2,

我们拿上面的圈来想象计算的过程,绕着圈顺时针走步,圈上的每一个数字对应一个走步,计算(-1)+2,也即在(-1)处走两步,我们走到+0处。由于正常的运算中没有两个0,我们在最后回加一个1。

由于溢出,得到的结果等于0(0000 0000),就是因为中间-0占用了一个计数,我们把进位1加最后结果上,得到正确结果1:

1111 1110

  +0000 0010

=========

(1)0000 0000

          + 1

=========

0000 0001

   

减法可以用加法来表示。

   

三、2的补码Two\’s complement——补码

1、从1的补码的角度来理解补码加法

负数的补码经常用反码加一来计算。将补码与1的补码加法联系起来可以理解为什么可以用补码加法代替减法。

将1的补码中-0的编码空出来给-1用,后续数值依次前移,最后1000 0000会空出来,我们给它分配-128。因此,我们可以取值[-1,-128]U[0,127]。

   

因此,补码根据反码(即前文1的补码)加一来计算其实就是把-0挤掉,留了个位置给-128。

Binary value

Two\’s complement interpretation

Unsigned interpretation

00000000

0

0

00000001

1

1

01111110

126

126

01111111

127

127

10000000

128

128

10000001

127

129

10000010

126

130

11111110

2

254

11111111

1

255

   

From <http://en.wikipedia.org/wiki/Signed_number_representations#Ones.27_complement>

那为什么可以用补码来计算减法呢?理解了反码的加法,其实就不难理解为什么补码可以直接用来计算减法了。

   

2、快速写出补码

正数的补码是它本身。

负数的补码:

从右往左看,从第一个1出现的位置的下一位开始,反转比特,符号位不反转。比如(11010110) 的补码为10101010。

   

参考资料

【1】http://en.wikipedia.org/wiki/Signed_number_representations#Sign-and-magnitude_method

版权声明:本文为codetravel原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/codetravel/p/4361345.html