计算机的整型数可以分为有符号数和无符号数。有符号数使用最高位作为符号位。以一个字节(8bits)的存储为例。无符号为表示区间为[0, 255], 有符号数为[-128, 127]。
计算机二进制表示中,整数常用补码表示。正整数时补码和原码是等价的。
原码表示法
使用最高位作为符号位,其余位按照正常的换算方式。例如+1,-1分别表示为0000 0001,1000 0001。两者只有符号为不同。原码表示的区间为[-127, +127]。
补码表示法
正数的补码等于原码,负数的补码等于对应正数的取反加一。
举例来说,+127的补码为0111 1111,-127的补码是1000 0001。
这让负数补码看起来很反直觉。给定一个负数补码,我想知道对应的值,得减一取反得到绝对值再加上负号。这有的加一,有的减一的完全反应不过来。
其实有一种符合直觉的看法,先定义正数补码然后根据正数定义负数补码。基于
为正数,就是负数,上式表示为正数加上负数。假设我们已经定义好了正数补码,比如+127(0111 1111)。我们将负数补码表示定义为与对应正数补码表示相加等于零的数。因此负数应该表示为(1000 0001),这就是-127。
1000 0001的计算步骤分为两步,
- 第一找到一个数与对应正数相加使二进制变成全1。
这是得到1000 0000。 - 接着再让1111 1111溢出,变成真正的零值。
把溢出位置扔掉就是最终的结果。
上面是从正数算负数,那么从负数算正数也可以使用同样的办法,压根不用考虑到底是加一还是减一。
举个例子:
-127(1000 0001) -> 0111 1110+0000 0001->0111 1111(+127)
+127(0111 1111)-> 1000 0000 + 0000 0001 -> 1000 0001(-127)
所以不管从正数推负数还是负数推正数都只用加一操作。
用正负相加等于零的方式还带来一个好处,不需要减法。
对任意 看成,例如:
做减法的时候如果不够减就向高位借,实在不够就向溢出位借。
总结上述操作就是先定义正数,使用的办法来定义对应负数,并且等于零是用数值溢出的办法得到的,计算时将减法转换为加法,同时上述操作符号位也都参与计算。
以上的操作似乎很一致,并且符合直觉。但是有一个数是例外,-128。8bits 补码表示的区间是[-128, 127], +128是没有定义的,-128也就没办法用上述的办法定义。
用补充的办法将这个边界值定义为 1000 0000,试着用上面的办法计算其对应的值,我们发现其对应正值还为1000 0000,也就是说符号为仍为1。所以这个边界值是上述转换方法的一个bug,但是不影响计算,因为符合借位计算的思想,也符合加周期取模的思想。
来源:CSDN
作者:呆坐的熊
链接:https://blog.csdn.net/Lin_RD/article/details/104573650