例如,有长型的3个变量,我们添加a和b并获得s:
long a, b, s;
...
s = a + b
Run Code Online (Sandbox Code Playgroud)
现在((s^a) < 0 && (s^b) < 0)意味着什么?
我在Python 的源代码中看到了这样的支票:
if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
/* INLINE: int + int */
register long a, b, i;
a = PyInt_AS_LONG(v);
b = PyInt_AS_LONG(w);
i = a + b;
if ((i^a) < 0 && (i^b) < 0)
goto slow_iadd;
x = PyInt_FromLong(i);
}
Run Code Online (Sandbox Code Playgroud)
这段代码错了.
假设对于有符号整数,按位XOR的通常2的补码规则,那么
(s^a) < 0
Run Code Online (Sandbox Code Playgroud)
如果是的情况下s,并a具有其符号位设置为相反的值.从而,
((s^a) < 0 && (s^b) < 0)
Run Code Online (Sandbox Code Playgroud)
指示s已登录从两个不同的a和b,然后其必须具有等号(假装0是正的).如果你添加了两个等号的整数并得到了不同符号的结果,那么一定有溢出,所以这是一个溢出检查.
如果我们假设有符号溢出包装,则s具有相反的符号,a并且b恰好在发生溢出时.但是,签名溢出是未定义的行为.计算s已经错了; 我们需要检查是否在没有实际执行操作的情况下发生溢出.
Python不应该这样做.您可以在Python 2源代码中int.__add__看到它应该做的事情:
/* casts in the line below avoid undefined behaviour on overflow */
x = (long)((unsigned long)a + b);
if ((x^a) >= 0 || (x^b) >= 0)
Run Code Online (Sandbox Code Playgroud)
它应该转换为unsigned以获得定义的溢出行为.由于Python bug跟踪器上的问题7406,在5个不同的位置引入了转换为无符号的修复,但看起来它们错过了一个位置,或者INPLACE_ADD从那时起可能已经改变了.我在跟踪器上留言了.