原码and反码and补码

原码

原码表示

原码的首位表示其符号位,整数为0,负数为1,因此表示数据的有效为 7 位,两个字节最多能表示的数据范围是:-128~128。

正数的原码是其本身。假如整数占用两个字节,5 的二进制表示为:0000 0101,其原码为:0000 0101

0000 0000 和 1000 0000 分别表示 +0 和 -0。

负数的原码首位为1,其余位为其原本的二进制表示。-3 的原码 1000 0011

原码计算

如果基于原码进行计算会是什么结果呢?

正数的情况:5 + 3

5 + 3

  0000 0101
+ 0000 0011
= 0000 1000 (8)

正数的情况下结果正确。

加上负数的情况:5 - 3 = 5 + (-3)

5 + (-3)

  0000 0101
+ 1000 0011
= 1000 1000 (-8)

5 - 3 = -8,结果错误,原码不能对负数直接进行计算。

反码

正数的反码是其本身。5 的反码 = 0000 0101。

负数的反码符号位不变,其余位取反。-3 的反码 = 1111 1100。

0000 0000 和 1000 0000 分别表示 +0 和 -0。

负数 t 的反码 = 1111 1111 - t(符号位不变)。

使用反码进行计算:

正数的情况显然结果与原码一致,结果正确。

负数的情况:5 - 3

5 - 3 = 5 + (-3)

  0000 0101
+ 1111 1100
= 0000 0001 (1)
10 - 5

  0000 1010
+ 1111 1010
= 0000 0100 (4)    

负数的场景结果与正确结果差1。

补码

正数的补码是其本身。5 的补码 = 0000 0101

负数的补码是其反码 +1。-3 的补码 = 1111 1101

使用补码进行运算,正数的场景与之前一致,结果正确。

5 - 3 = 5 + (-3)

  0000 0101
+ 1111 1101
= 0000 0010 (2)
10 - 5

  0000 1010
+ 1111 1011
= 0000 0101 (5) 

使用补码时刚好多减去反码计算中的差值1,所以结果是正确的。

负数的补码等于反码 +1 的解释

看网上的解释都是从时间的计算考虑。时钟每12个小时循环一次,假设当前时间为9点,要将时针调到1点,可以将时间“往前”增加 4 个小时,也可以将时间“往后”拨 8 个小时。即:

9 + 4 = 9 - 8

意思就是说在这个运算中 -8 的运算等价于 +4,每一个负数都可以找到一个对应的正数,使计算结果不变。

10 - 5 = 10 + 7
10 - 6 = 10 + 6

这两个等价数的绝对值和等于 12。

考虑更普遍的场景,假设当前时间为 a 点,将时间往回调 t 小时,等价于将时间往前拨 12 - t 小时:

a - t = a + 12 - t

以公式a - b = a + mod - b 为例,

设: -b = 10000101 (-5的二进制),

模(mod) = 11111111

a - b = a + (-b的补数) = a + (mod - b) = a + (11111111 - 10000101)

由于二进制的特性,11111111减去10000101得到补数11111010,实际就等于 10000101 各位取反——也就是反码,如下对应关系:

11111111 #模(mod)

10000101 # -5二进制

11111010 #-5二进制补数(即反码)

(这里要注意第一位符号位是不变的)

但是在实际计算中补数(反码)还必须加1——即:

11111010 +1(00000001)= 11111011

这是因为在8位二进制系统中,模=2^8=256,表示为二进制是100000000,是九位二进制数,在8位二进制系统中实际产生溢出位无法计算,而8位二进制系统计量范围是02^n-1,也就是0257,最大值是257,也就是我们上面计算中模的取值二进制11111111,也就是说为了便于计算,我们是用257的二进制11111111,代替了模256的实际值100000000,那么在实际计算中取得补数后,必须加上这个差值1才不会出错,即:

补数(反码)+1=补码

这就是为什么补码加1的原因。