参考资料 我们为什么需要反码和补码?
大学在C语言课上,第一次接触原码、反码、补码,觉得很奇怪,我们用二进制表示,然后直接加减乘除就好了。要搞明白这些码,要从二进制开始。
人类钟意于十进制的历史,有人说是因为10个手指头(也可能是10个脚趾头,手动狗头),阿拉伯人改进了印度的十进制,后来传到了欧洲,后面变成世界标准。
人类习惯了十进制,但电子设备有自己的角度,就像那些动物如果没有10根手指,大概率也不会产生10进制,而是会产生比如20进制,12进制。
二极管是人类最开始发明的比较简单的电子元器件,这里面只有开 关两种状态,这也是我们采用二进制的原因,当然现在电子元器件可以有三种状态,但标准已经制定完了,再换成三进制的代价太大了。
二进制也可以进行加减运算啊,为什么要设计这么多码,这一切都是因为我们CPU中只有加法器。加法器可以实现加减乘除的运算,同时用电路实现减法器 乘法器 除法器都非常复杂,既然可以用加法来实现所有的加减乘除操作,那么从成本考虑只保留了加法器。
既然我们选择了省钱,那么就要做好在另一个方面复杂度提高的心理准备,“好快省”是不可能同时存在的,我们要尊重科学规律、自然规律。
就一般的整数计算来说,包括正数和负数。对加法来说,包括以下几个可能的情况
- 正数 + 正数
- 正数 + 负数
- 负数 + 负数
首先,我们该怎么表示负数呢?
为了表示负数,我们要牺牲一个存储位,用它来表示符号,1个8 bit的数据,最高位为符号位,其中1代表负数,0代表正数,表示范围为[-128, 127]。
其中
0000 0000 = 0
1000 0000 = -128
这里我有一个疑问,为什么表示范围不是[-127, 127],1000 0000不应该也是 0 吗,1000 0000 1000 0000 也可以用来-128啊,干嘛要弄的这么复杂。
首先8 bit的空间,在电路上来说肯定是2^8 = 256种状态,那我们没有必要用两种电路状态表示同一个数字。另外,用16位来表示-128也浪费空间了。最后,第1位毕竟是1,用来表示-128更好,而不是表示128。
回到加法。
正数 + 正数 没什么需要讨论,主要是讨论 后面两种情况。
正数 + 负数
1 - 2 = 1 + (-2) = 0000 0001 + 1000 0010 = 1000 0011 = -3
结果与预期不符合。
二进制计算溢出有一个特点,就是它类似于模运算。
127 + 1 = 1111 1111 + 0000 0001 = 1000 0000 = -128

如上述所示,就是看作去模数/余数 的操作,也就是一个数,无论正负,加上模,得到的仍然是它自身。
也就是说 i - j = i + (-j) = i + (-j) + 2^n -1 + 1 = i + (2^n - j - 1) + 1
这里我们在计算2^n - 1和 j 的时候,没有符号位
所以 2^n - 1 - j = 1111 1111 - 0000 0010 = 1111 1101
看起来就是-j = 1000 0010保持符号位不变,其它位置取反,这里我们称 1111 1101 为-2的反码,那么 反码 +1 就是我们想要的结果了,最终得到1111 1111,这是反码的计算结果,最终我们要把反码转变成原码,那就是先减去1,然后保持符号位不变,其它位取反,最终得到 1000 0001。与预期结果相符合。
CSAPP Chapter2 信息的表示和处理
练习题 2.13
用bis bic实现bool_or bool_xor
1
2
3
4
5
6
7
8
9
10
11
12int bis(int x, int m);
int bic(int x, int m);
int bool_or(int x, int y)
{
int result = bis(x, y);
return result;
}
int bool_xor(int x, int y)
{
int result = bic(bis(x, y), bis(bic(0xFF, x), bic(0xFF, y)));
return result;
}
关于bool_xor = (x | y) & (~x | ~y),这个跟模拟电路的与非门概念很接近,很多的功能都是通过简单的与非门实现的。
我们知道1 xor 1 = 0 xor 0 = 0, 1 ^ 0 = 0 ^ 1 = 1
输入是x y, 输出是1个z,这时候中间肯定还有一层(类似于神经网络),只是这一层的我们不知道有几个系数,最后输出z的公式是什么。有可能是非常简单的&,|,也有可能是非常复杂的一个公式,比如用到了好几个与非,打个比方,中间这一层有N个系数,然后通过一个非常复杂的与非门设计。
幸好,异或门没有那么复杂。 我们先做一个假设,中间一层也是两个输入,最后通过 & 操作输出。好吧,我写不出来我的思考过程,或者说很难用简练的语言说出来。
回到bool_xor,我们要首先实现bit_not 和 bit_and
1
2
3
4
5
6
7
8
9
int bit_not(int x)
{
return bic(0xFF, x);
}
int bit_add(int x, int y)
{
return bic(x, bit_not(y));
}
练习题 2.15
只使用位级和逻辑运算,编写一个C表达式,它等价于 x==y。换句话说,当x和y相等时它将返回1,否则就返回0。
! (x ^ y)