符号 | ||||||||
---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 127 |
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 2 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | −1 |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | −2 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | −127 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | −128 |
原码,反码,补码
- 正数的原码,反码,补码相同
- 负数的反码:符号位不变,其余位取反
- 负数的补码:反码 + 1
CPU 可以很快的计算加法,但是减法、乘法、除法就复杂得多。
减法的计算:1 - 1 = 1 + (-1)
如果带上符号,CPU 需要区分符号位,计算起来就复杂了。
1 + (-1) = [0001]原 + [1001]原 = [1010]原 = -2
所以发明反码,解决了符号位的问题。
1 + (-1) = [0001]反 + [1110]反 = [1111]反 = [1000]原 = -0
其背后肯定有计算机科学专家的严密论证。
原码和反码的问题:+0,-0
所以发明补码,将数据表示范围从 [-7, 7]
-> [-8, 7]
。
为了方便,还是以四位二进制为例说明:
数字 | 原码 | 反码 | 补码 |
---|---|---|---|
0 | 0000 | 0000 | 0000 |
1 | 0001 | 0001 | 0001 |
2 | 0010 | 0010 | 0010 |
3 | 0011 | 0011 | 0011 |
4 | 0100 | 0100 | 0100 |
5 | 0101 | 0101 | 0101 |
6 | 0110 | 0110 | 0110 |
7 | 0111 | 0111 | 0111 |
-1 | 1001 | 1110 | 1111 |
-2 | 1010 | 1101 | 1110 |
-3 | 1011 | 1100 | 1101 |
-4 | 1100 | 1011 | 1100 |
-5 | 1101 | 1010 | 1011 |
-6 | 1110 | 1001 | 1010 |
-7 | 1111 | 1000 | 1001 |
原码还剩最后一种组合:-0,也就是 1000
,对应反码 1111
。补码还剩 1000
。
但是补码 1000
在我们的 CPU 加法器中可以起到 -8 的作用:
(-1) + (-7) = [1111]补 + [1001]补 = [1000]补
(-2) + (-6) = [1110]补 + [1010]补 = [1000]补
补码的计算:
1 + (-1) = [0001]补 + [1111]补 = [0000]补 = [0000]原 = 0
背后肯定也是有严密论证的。
所以,现在,计算机内部的计算都是以补码形式进行。
乘法的实现
除法的实现
取余的实现
逻辑运算
逻辑运算 | 数学表示 | 编程 |
---|---|---|
与 | $a \land b$ | & |
或 | $a \lor b$ | | |
非 | $\neg a$ | ! |
异或 | $a \oplus b$ | ^ |
左移位 | $a \ll 4$ | << |
右移位 | $a \gg 4$ | >> |