今天读CSAPP, 看到一道题, 蛮有意思的.
题中提到. 在上世纪后期非常流行的 Digital Equipment 的 VAX 计算机. 它没有布尔运算 AND 和 OR, 只有bis(位设置) 和 bic(位清除) 两种指令.
这两种指令都接受两个参数, 数据字X 与 掩码字M.
bis 代表将掩码字M中, 每一个为 1 的位置, 其在数据字X上对应位置设置为 1.
例如 X: 11000011, M: 10101010. 因为M中1, 3, 5, 7位置上为1, 所以将X, 1, 3, 5, 7位置设置为1. 结果为11101011
bic 代表 将掩码字M中, 每一个为 1 的位置, 其在数据字X上对应位置设置为 0.
bic 与 bis 语义相反.
我们现在假设我们使用着 VAX, 正要考虑如何用这两个原语实现逻辑操作 OR, AND, XOR.
int bis(int x, int m);
int bic(int x, int m);
先考虑OR
bis 是将所有为1的位置, 都强制写为1, 和当前值为0或1没有任何关系.
也就是当n表示一个位置, (Mn=1) bis (Xn=0/1) = 1
得出 1 bis 0 = 1
和 1 bis 1 = 1
. 这和 OR 的特点完全一致.
int bool_or(int x, int y) {
return bis(x,y);
}
其次是AND
bis 的1 bis 0/1 = 1
并不能解释 AND , 当已知一边为1时, AND 的结果仍无法确定. 但我们知道, AND 和 OR 是相反的, OR 是当两边都为0时,结果才为0, 而 AND 是当两边都为1时, 结果全为1.
我们可以得出, 0在 AND 是起绝对作用的. 当一边为0时, 结果一定为0.
那么, 如果构建出 "当一边为0时, 结果一定为0" ?
我们拿一个掩码举例, 例如 10101010
, 它作用的位置是1, 3, 5, 7号位. 因为bis和bic都是通过1的位置来操作数据. 那么如何操作0的位置? 也就是说, 如何让bis和bic操作2, 4, 6, 8这四个位置, 因为这里有我们非常重要的 "0"
.
好, 现在说了这么多, 思路可能有点乱, 让我们理清下现有的内容.
bis 会将掩码中 1 所在的位置, 将数据同样位置强制设置为 1 .
bic 会将掩码中 1 所在的位置, 将数据同样位置强制设置为 0 .
and 操作期待将掩码中 0 所在的位置, 将数据同样位置强制设置为 0
那么bic(X, 0b10101010)
是将1, 3, 5, 7 位置设置为0
而AND(X, 0b10101010)
理应将2, 4, 6, 8位置设置为0;
那么.....答案显而易见了.
int bool_and(int x, int y) {
return bic(x, ~y);
}
再然后是XOR
XOR比较容易, 因为存在等式, xor = (a & ~b) | (~a & b)
int bool_xor(int x, int y) {
return bool_or(bool_and(x, ~y), bool_and(y, ~x));
}
我们将其替换一下
int bool_xor(int x, int y) {
return bis(bic(x, ~~y), bic(y, ~~x));
}
最终版
int bool_xor(int x, int y) {
return bis(bic(x, y), bic(y, x));
}
我们现在来用C模拟下这两个原语, 因为我们现代的机器不是VAX, 所以仅仅是 模拟. 其实就是上述的逆操作
int bis(int x, int m) {
return x | m;
}
int bic(int x, int m) {
return x & ~m;
}