原码、反码、补码
原码
1 | 1 => 00000001 |
当一个正数与一个负数的原码做加法时:
1 | 1 + (-1) |
异号数字的原码相加,原码的符号位一定为1,和实际情况不服,比如: 2 + -1。
反码
1 | // 正数 1 |
正数的反码就是原码本身,负数的反码就是原码除符号位外,按位取反。
1 | 1 + (-1) |
反码相加,解决了相反数之间相加的问题,但
1 | 2 + (-1) |
计算结果仍有问题
补码(仍需完善)
正数的补码等于原码,负数的补码等于原码自低位向高位,第一个出现的‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。
比如在4位系统下:
1 | 2 + (-1) |
将原码计算转换为补码计算:
2 + (-1)
=2 + (2^4-1)
二进制编码 | 十进制 | 描述 |
---|---|---|
0000 | 0 | |
0001 | 1 | << 结果位置 |
0010 | 2 | << 被减数位置 |
0011 | 3 | |
0100 | 4 | |
0101 | 5 | |
0110 | 6 | |
0111 | 7 | |
1000 | -8 | |
1001 | -1 | |
1010 | -2 | |
1011 | -3 | |
1100 | -4 | |
1101 | -5 | |
1110 | -6 | |
1111 | -7 |
从上面的表格可以看出,被减数-减数
可以看成被减数+减数的同余数
,因为在计算机中,最大值溢出后会从0开始重新计数,所以做减法时,可以看成从被减数一直加,加到溢出后,从0开始再加,直到加到结果值位置。累加的总值就是补码的编码(可以理解为补码没有符合位,只是个偏移量),即通过补码=2^n-减数
算出补码在十进制的数值,再将补码转成对应的二进制编码。
当被减数+补码
的值为正数时,因为都是从0累加,所以计算的结果和预期结果值一致,不需要转换。
而当被减数+补码
的值为负数时,比如:2 - 4 = -2
=> 2 + (16 - 4) = -2
=> 2 + 12 = -2
二进制编码 | 十进制 | 描述 |
---|---|---|
0000 | 0 | |
0001 | 1 | |
0010 | 2 | << 被减数位置 |
0011 | 3 | |
0100 | 4 | |
0101 | 5 | |
0110 | 6 | |
0111 | 7 | |
1000 | -8 | |
1001 | -1 | |
1010 | -2 | << 结果位置,记作R2 |
1011 | -3 | |
1100 | -4 | |
1101 | -5 | << R2的反码,记作R3 |
1110 | -6 | << 被减数+补码 位置,记作R1 |
1111 | -7 | |
10000(仅用作参考,无符号位,无位数限制) | 2^4 | 模 |
2-3=-1 但实际计算出来的编码转化为十进制为-7
2-4=-2 但实际计算出来的编码转化为十进制为-6
可以看出,将预期值的绝对值作为偏移量,实际计算出的二进制值(1110)加上偏移量(0010),刚好等于模(10000)
因为补码+负数的绝对值等于模
,且R1+|R2|=模
,所以R1
是R2
的补码,所以两数相减为负数时实际计算出来的是结果的补码,要得到原码,需要将补码转换为原码,但1000(补)没有原码,直接就表示原值-2^n
。
-2可以看成1010=1000+0010(正向偏移),-2的反码可以看成1101=1111-0010(反向偏移)
补码等于反码+1:R3+|R2|+1=模=R1+|R2| => R3+1=R1
类似-2-4
这类的,可以看作是(0-2)-4
,先算出-2位置的值,再算出-2-4位置的值。
1 | 2 + (2^4-1) // 在8位系统下,2^4-1 就是 -1 的补码,负数的绝对值+补码=2^n(n为位数) |