原码
原码表示
原码的首位表示其符号位,整数为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的原因。