原码、补码

佐手、 提交于 2020-03-02 10:21:38

计算机的整型数可以分为有符号数和无符号数。有符号数使用最高位作为符号位。以一个字节(8bits)的存储为例。无符号为表示区间为[0, 255], 有符号数为[-128, 127]。
计算机二进制表示中,整数常用补码表示。正整数时补码和原码是等价的。

原码表示法

使用最高位作为符号位,其余位按照正常的换算方式。例如+1,-1分别表示为0000 0001,1000 0001。两者只有符号为不同。原码表示的区间为[-127, +127]。

补码表示法

正数的补码等于原码,负数的补码等于对应正数的取反加一。
举例来说,+127的补码为0111 1111,-127的补码是1000 0001。
这让负数补码看起来很反直觉。给定一个负数补码,我想知道对应的值,得减一取反得到绝对值再加上负号。这有的加一,有的减一的完全反应不过来。
其实有一种符合直觉的看法,先定义正数补码然后根据正数定义负数补码。基于
x+(x)=0x+(-x)=0
xx为正数,x-x就是负数,上式表示为正数加上负数。假设我们已经定义好了正数补码,比如+127(0111 1111)。我们将负数补码表示定义为与对应正数补码表示相加等于零的数。因此负数应该表示为(1000 0001),这就是-127。
1000 0001的计算步骤分为两步,

  • 第一找到一个数与对应正数相加使二进制变成全1。
    01111111+10000000=111111110111\quad1111 + 1000\quad0000=1111\quad1111
    这是得到1000 0000。
  • 接着再让1111 1111溢出,变成真正的零值。
    01111111+10000000+00000001=11111111+00000001=1000000000111\quad1111 + 1000\quad0000 + 0000\quad0001 \\ = 1111\quad1111 + 0000\quad0001 \\ =1\quad0000\quad0000
    把溢出位置扔掉就是最终的结果。
    上面是从正数算负数,那么从负数算正数也可以使用同样的办法,压根不用考虑到底是加一还是减一。
    举个例子:
    -127(1000 0001) -> 0111 1110+0000 0001->0111 1111(+127)
    +127(0111 1111)-> 1000 0000 + 0000 0001 -> 1000 0001(-127)
    所以不管从正数推负数还是负数推正数都只用加一操作。

用正负相加等于零的方式还带来一个好处,不需要减法。
对任意 xyx-y看成x+(y)x+(-y),例如11271-127:
1127=(00000001)(01111111)=10000010=1+(127)=(00000001)+(10000001)=100000101-127=(0000\quad0001) - (0111\quad1111)\\ =1000\quad0010\\ =1+(-127)\\ =(0000\quad0001)+ (1000\quad0001)=1000\quad0010\\
做减法的时候如果不够减就向高位借,实在不够就向溢出位借。
总结上述操作就是先定义正数,使用x+(x)=0x+(-x)=0的办法来定义对应负数,并且等于零是用数值溢出的办法得到的,计算时将减法转换为加法,同时上述操作符号位也都参与计算。

以上的操作似乎很一致,并且符合直觉。但是有一个数是例外,-128。8bits 补码表示的区间是[-128, 127], +128是没有定义的,-128也就没办法用上述的办法定义。
用补充的办法将这个边界值定义为 1000 0000,试着用上面的办法计算其对应的值,我们发现其对应正值还为1000 0000,也就是说符号为仍为1。所以这个边界值是上述转换方法的一个bug,但是不影响计算,因为符合借位计算的思想,也符合加周期取模的思想。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!