CSAPP
信息的处理和表示
习题
2.31 双重溢出
1 | /* Determine whether arguments can be added without overflow */ |
核心原因:无论 x + y 是否溢出,表达式 (sum - x == y) 和 (sum - y == x) 在补码运算中永远恒为真(1)。
该函数对于任何输入都会返回 1,完全起不到检测溢出的作用。
原理解析:模算术下的阿贝尔群
补码的加法和减法在机器字长为 $w$ 位(如 32 位)的底层硬件中,本质上都是在模 $2^w$ 的整数环(数学上的阿贝尔群)中进行的。
加法运算:
$$\text{sum} = (x + y) \bmod 2^w$$紧接着的减法运算:
由于模运算满足结合律与逆元性质:
$$\text{sum} - x \equiv ((x + y) - x) \pmod{2^w} \equiv y \pmod{2^w}$$
加法溢出所产生的位回绕,会被后续减法溢出所产生的位回绕完全抵消。
具象数值推导(以 4 位补码为例)
在 4 位补码系统中:
- 表示范围:$[-8, 7]$
- 模数:$2^4 = 16$
设:$x = 5$,$y = 4$(真实和为 $9 > 7$,发生正溢出)。
计算
sum:
$$5 + 4 = 9 \xrightarrow{\text{模 16 截断}} 9 - 16 = -7$$
内存中sum的实际值为 $-7$。计算
sum - x:
$$\text{sum} - x = -7 - 5 = -12$$
$-12$ 超出下界,发生负溢出并做模 16 回绕:
$$-12 + 16 = 4$$
计算结果为 $4$。执行判断:
计算得到的sum - x为 $4$,而参数y的值也是 $4$:
$$(4 == 4) \implies \text{True}$$
即使发生了严重溢出,(sum - x == y) 依然判定为真。
正确的实现方式
不能通过事后减法来反推,而应该通过操作数与结果的符号位变化来判定:
1 | /* 正确检测补码加法溢出的实现 */ |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 RyanLiu 的博客!
