1的补码及2的补码
⼀、计算机的负数表⽰
⼆、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+00
0000000111
⋮⋮⋮
01111101125125
01111110126126
01111111127127
10000000−127128
10000001−126129
两个负数的补码相加10000010−125130
⋮⋮⋮
11111101−2253
11111110−1254
11111111−0255
以上表举例来看:(-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
0000000000
0000000111
⋮⋮⋮
01111110126126
01111111127127
10000000−128128
10000001−127129
10000010−126130
⋮⋮⋮
11111110−2254
11111111−1255
那为什么可以⽤补码来计算减法呢?理解了反码的加法,其实就不难理解为什么补码可以直接⽤来计算减法了。
2、快速写出补码
正数的补码是它本⾝。
负数的补码:
从右往左看,从第⼀个1出现的位置的下⼀位开始,反转⽐特,符号位不反转。⽐如(11010110) 的补码为10101010。
参考资料