例如,有3个长类型变量,我们添加a
和b
并获取s
:
long a, b, s;
...
s = a + b
现在((s^a) < 0 && (s^b) < 0)
是什么意思?
我在Python的source code中看到了这样的支票:
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);
}
答案 0 :(得分:5)
此代码错误。
假设对于有符号整数,通常的2位补码XOR补码规则,那么
(s^a) < 0
是s
和a
将其符号位设置为相反值的情况。因此,
((s^a) < 0 && (s^b) < 0)
表示s
的标志与a
和b
的标志不同,后者必须具有相同的符号(假装0为正)。如果你添加了两个等号的整数并得到了不同符号的结果,那么一定有溢出,所以这是溢出检查。
如果我们假设已签名的溢出包装,那么s
与a
和b
的符号恰好在溢出发生时相反。但是,签名溢出是未定义的行为。计算s
已经错了;我们需要检查是否会在没有实际执行操作的情况下发生溢出。
Python不应该这样做。您可以在Python 2 source code for 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)
它应该转换为无符号以获得定义的溢出行为。由于issue 7406在Python错误跟踪器上引入了5个不同位置的转换为无符号修复,但看起来它们错过了一个位置,或者从那时起可能INPLACE_ADD
被更改了。我已经在跟踪器上留言了。
答案 1 :(得分:2)
我不知道这是怎么可能的。
如果(s^a) < 0
,则s
或a
的符号位必须为1,因此s
或a
(但不是两者)必须是消极的。 s
和b
也是如此。因此,s
为负数,a
和b
均为正数,或s
为正数,a
和b
均为正数。这两种情况似乎都不可能。
除非你计算整数溢出/下溢,当然。