0%

补码完全指南

本文基于自己的理解,试图让猪都能理解补码的概念^_^。老规矩,先是几个概念。

机器数

机器数是将符号数字化的数,最高位是符号位,正数符号位为0,负数符号位为1,其余位表示数值。

真值

机器数所对应的实际数值。 

编程语言类型

无符号数

全部二进制位均代表数值,没有符号位。

有符号数

最高位代表符号位(“0”表示“+”,“1”表示“-”),其余位表示数值。

类型强转

在计算机系统中,数值一律用补码来存储。对于一串二进制数字,被解释为无符号数或有符号数它对应的真值可能是不相等的。比如内存中的一串二进制1111 1111,被解释为无符号数时则表示127,被解释为有符号数时则表示-1。在强转类型时特别要注意,特别是语言的隐式强转,很容易就出bug。

原码

  • 最高位为符号位,“0”表示正号,“1”表示负号。
  • 其余的n-1位表示数值的绝对值。
  • 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。
  • 数字“0”有“+0”和“- 0”两种表示形式:0000 0000(+0)、1000 0000(-0)。
  • 对于机器字长为n+1位的机器,原码表示法可表示的数值范围为:-2^n+1 ≤ X ≤ 2^n-1。比如8位字长,原码能够表示的数值范围为[-127, 127]。

反码

  • 最高位为符号位,“0”表示正号,“1”表示负号。
  • 其余的n-1位表示数值。
  • 正数的反码与原码相同。
  • 负数的反码则是在其原码的基础上, 符号位不变,其余各个位取反。
  • 数字“0”有“+0”和“- 0”两种表示形式:0111 1111(+0)、1111 1111(-0)。
  • 对于机器字长为n+1位的机器,反码表示法可表示的数值范围为:-2^n+1 ≤ X ≤ 2^n-1。比如8位字长,反码能够表示的数值范围为[-127, 127]。

补码

  • 最高位为符号位,“0”表示正号,“1”表示负号。
  • 数字“0”只有一种表示形式:0000 0000。
  • 正数的补码与原码相同。
  • 负数的补码是将其对应正数按位取反再加1。(ps:这不是规定,后面有推导)
  • 对于机器字长为n+1位的机器,补码表示法可表示的数值范围为:-2^n ≤ X ≤ 2^n-1。比如8位字长,补码能够表示的数值范围为[-128, 127]。这就有意思了,-128已经超出了原码和反码的表示范围,因此-128有补码表示(1000 0000)但却没有对应的8位字长的原码和反码表示。所以计算一个数的补码最好不要用大部分网上说的先算原码再算反码啥的,先算原码就会有特例-128,运算规则就不统一,而且还很难理解-128的补码。最好就是用上面的办法。

eg:

1.已知真值求补码

正数的补码与原码相同。负数的补码是将其对应正数按位取反再加1。

求-1的补码。-1的绝对值为1,二进制为0000 0001,按位取反得到1111 1110,再加1,得到补码1111 1111。

求-128的补码。-128的绝对值为128,二进制为1000 0000,按位取反得到0111 1111,再加1,得到补码1000 0000。

2.已知补码求真值

先看最高位,确定符号,如果是0,则直接算得其真值。如果为负数则按位取反,再加1,得到其真值的绝对值,符号+绝对值得到真值。

求补码1000 0000的真值。最高位为1,说明真值是一个负数,按位取反得到0111 1111,再加1,得到真值的绝对值1000 0000即128,所以最终真值为-128。

求补码1111 1111的真值。最高位为1,说明真值是一个负数,按位取反得到0000 0000,再加1,得到真值的绝对值0000 0001即1,所以最终真值为-1。

可以看出对一个负数的补码再取反加1得到的就是负数的绝对值。

为什么使用补码

在有模的计量系统中,减一个数等于加上它的补数。比如3 - 1 = 3 + [-1]补 = 3 + 255 = 0000 0011 + 1111 1111 = 0000 0010(最高位丢弃) = 2

上面这个例子可能不是很好理解,一般都是用时钟系统来举例。

假设现在是3点钟,逆时针旋转代表减,顺时针旋转代表加,那么- 1,就是逆时针旋转1格,指向2,所以3 - 1 = 2。事实上我们可以顺时针旋转11格,同样最终会指向2。因此在这个系统里 3 - 1 等价于 3 + 11。11就是-1的补码。

有了补码,我们就可以使用加法来计算减法了。就不用单独设计一套减法器了,直接使用加法器就可以了。

玩过DNF PK的人都知道,要想成为一个PK高手,计算对手的蹲伏时间、萝莉的草人时间非常重要,并且还要计算的非常快,如果你计算的不快可能都忘了对手什么时候交的蹲伏了。如果你熟悉补码的话,那你就可以计算的非常快。

比如对手3分18秒交的蹲伏,20秒后蹲伏冷却。那么他下一次冷却的时间就是3分18秒 - 20秒 = 2分58秒。如果你直接去算减法的话可能会很绕。算加法就很简单,秒时间是一个模为60的系统,-20就相当于+40,因此18 - 20 = 18 + 40 = 58,很快就算出来对手的下一次蹲伏是58秒。分钟一般不用考虑。

再比如魔道的草人是48秒,如果对手3分13秒交的草人,那么他下一次冷却的时间就是3分13秒 - 48秒,-48相当于+12, 因此 13 + 12 = 25,即2分25秒魔道的草人再次冷却。如果秒的倒计时大于48就不要用补码去算了,比如3分52秒,52 - 48 = 4。当然如果你想用补码去算也没有问题,52 + 12 = 64,6是进位舍去,结果也是4。

补码的计算方式由来

为什么负数的补码是将其对应正数按位取反再加1?

对于8位机器字长,它的模就是256,一个负数的补码就等于模加上这个负数。其实正数的补码也等于模加上这个数只不过舍去进位就是正数自身,所以正数的补码就是其原码。

比如:-1,它的补码就是256+(-1) = 256 - 1 = 255 - 1 + 1 = 1111 1111 - 0000 0001 + 1 = 1111 1110 + 1 = 1111 1111

1111 1111减去一个正数A等价于A按位取反。因此一个负数的补码是将其对应正数按位取反再加1。

加减法电路

如图:

上图表示的是一个4位加减法器,可以计算加法和减法。

输入为A3A2A1A0,B3B2B1B0,输出为S3S2S1S0。K信号代表加减操作,K=0表示做加法,K=1表示做减法,同时K也作为输入。三角形为异或门,矩形为全加器。该电路主要部件还是加法器,配合异或器和加减标志信号后,就可以做减法了。可以看到两个数的减法最终被转换为另两个数的加法,避免了单独设计减法器。

比如5 - 2 = 0101 - 0010,因为是减法所以K=1,0010与1异或,其实就是按位取反,同时K作为输入相加,正好就是加1。所以5 - 2 = 0101 - 0010 = 0101 + 1101 + 1 = 0101 + 1110 = 5 + [-2]补 = 10011,因为是4位机最高位舍去,结果就是0011,也就是3。

再比如5 + 2,因为是加法K = 0 ,0010与0异或,其实就是不变,真是太巧了。直接加就完了。0101 + 0010 = 0111 = 7。

再比如4 - (-1) ,由于负数是以补码存储的,所以4 - (-1) = 0100 - 1111 = 0100 + 0000 + 1 = 0100 + 0001 = 4 + 1 = 5。

再比如3 + (-1) = 0011 + 1111 = 10010,最高位舍去,结果就是0010 = 2。

int i = -1; => i = 0 - 1 = 0000 + 1111 = 1111。1111存入内存。上述过程其实就是把1进行按位取反再加1,这正是补码的算法。以上电路实现了整数以补码进行存储。

觉得文章有帮助可以打赏一下哦!