补码
引言
在查看int与unsigned long的内存时,发现对补码的规则已经模糊不清了,又从头看了一遍,有了更深的理解,故记录。
负数的表示
在数字设计中,一个二进制数的最高位(MBS)被称作符号位。MBS为0,表示为正数;MBS为1,表示为负数。基于此,有两种常见的负数表示方法:
- 补码:负数 = 正数原码逐位取反+1,例如$011_2 = 3_{10},101_2 = (-3)_{10}$
- 反码:负数 = 正数原码逐位取反,例如$010_2 = 2_{10},101_2 = (-2)_{10}$
但是补码为什么要按如此规则设计呢,我将对此在下文给出自己的理解。
Motivation:加法实现减法
在计算机中,负数多采用补码表示,这是因为补码能用加法实现减法。
表针的例子
表盘上的表针,就是加法实现减法的一个例子。表针在表盘上按顺时针轮回转动,起初指向3,后面会重新指向0。这并不是靠按逆时针方向转动实现的,而是靠继续顺时针转过9格,实现了归0。
如果用数学语言描述上述过程,可以得到如下的两个“等式”:
$$3 - 3 = 0$$
$$3 + 9 = 0$$
在表盘上,对于表针来说$3 + 9 = 12$并不成立,这是因为表盘刻度的范围是:0~11。而12,溢出了表盘的范围。
Key idea:溢出
受到表针例子的启发,不难想到:可以利用二进制数的溢出特性达到加法实现减法的效果。
考虑3bit的二进制数,从000开始依次加1,可以枚举出所有的可能:
-->
000 001 010 011
| |
111 110 101 100
<--
从枚举的结果不难看出,无论从哪个数开始(假设从$X$开始),沿着顺时针方向前进8($2^3 = 7+1$)步,就会回到原点,用数学语言描述该过程为:
$$X + 8 - 8 = X$$
$-8$代表着溢出。
如果要实现减法($-Y$),那只需沿着顺时针方向少走$Y$步,即($8-Y$)步。用数学语言描述:
$$X - Y = X + (8 - Y) - 8$$
在上式中,令$\tilde{Y} = (8 - Y)$,则可以将上式改写为:
$$X - Y = X + \tilde{Y} - 8$$
在计算机中,溢出发生后会自动抛弃多的进位,也就是说$-8$是溢出后必然带来的结果。基于此,上式可以继续简写为:
$$X - Y = X + \tilde{Y} $$
至此,我们完成了:加法实现减法。
补码规则
现在,我们可以来分析补码为什么会定义如此的规则了:正数原码逐位取反+1。
首先,我们要对$\tilde{Y}$进行拆解重组:
$$\tilde{Y} = 8 - Y = 1 + (7 - Y)$$
在上式中,令$\hat{Y} = 7 - Y$。需要注意的是,我们现在考虑的是3bit的二进制数,$7_{10} = 111_2$。因此,可以得到:
$$\hat{Y} = 7 \oplus Y$$
异或的效果正是对Y逐位取反。最后:
$$\tilde{Y} = 1 + \hat{Y}$$
所以还有一个加一。
现在,我们应该可以更好地理解补码的定义了。
总结
之所以需要补码,是为了用加法实现减法,而这又需要靠溢出的特性实现,因此才有了相关的规则。