将INT_MIN从任何整数中减去是否被视为未定义行为?

3

如果我有这样的内容:

int a = 20;
int min = INT_MIN;

if(-a - min)
//do something

假设INT_MIN为正数时大于INT_MAX。编译器是否会将min转换为像-INT_MIN这样的东西,这可能是未定义的?

标识符“min”有一定的可能性会遮盖std::min(当有using语句时),因此我建议不要使用它。 - Brian Cain
1
@JonathanPotter 我认为这个问题的提问者完全理解有符号算术溢出是未定义的(这也是您建议的答案),只是担心一个看起来被定义了的二进制减法可能被解释为未定义的一元负号。 - Pascal Cuoq
@Steve 我对此没问题,但这是否被视为不良惯例呢? - user2672807
@BrianCain - using namespace std; 是错误的,最好的保护措施是不要使用它。在不使用 min 作为名称和不使用 using namespace std; 的选择中,后者显然更好。特别是当问题不是 minstd::min 冲突,而是 如果 您将 min 用作变量名,并且在没有限定符的情况下调用 std::min,则会出现错误。这个错误很容易修复;如果您无法自己删除 using namespace std;,那么在调用 min 的地方添加限定符。 - Pete Becker
@PeteBecker,我认为你对我的建议有些困惑。我同意你所说的一切,但这是一个虚假的二分法。我建议避免使用min,因为using namespace std(甚至是using std :: min)非常流行,我想你可能并不介意这种情况。我们中的许多人与其他人合作编写程序代码,这些人可能没有那么注意细节,为了避免产生不必要的困惑,让我们完全避免这个问题。 - Brian Cain
显示剩余8条评论
2个回答

2
您说得对,对于 INT_MIN 进行一元减运算可能是未定义的,但在您的示例中并没有发生这种情况。 -a - min 被解析为 (-a) - min。变量 min 仅涉及二元减法,第一个操作数只需要严格为负即可定义结果。
如果编译器将减法转换为其他内容,则其责任是确保新版本始终计算与旧版本相同的内容。

那么它基本上和 min + a 是一样的吗? - user2672807
@user2672807 当两个版本都没有溢出时,它与-(min + a)是相同的。我似乎找不到一个值,使得一个会导致溢出但另一个不会,但我不能确定(至少现在还不能)。 - Pascal Cuoq
2
@PascalCuoq:假设 x 是一个 int,并且假设使用二进制补码:当且仅当 xINT_MIN 时,-x 溢出。当且仅当 x 严格为负数时,INT_MIN + x 溢出。因此,当且仅当 x 严格为负数或零时,-(INT_MIN + x) 溢出。同时,当且仅当 -x 溢出或不是严格负数时,- x - min 溢出。也就是说,当且仅当 x 是零或负数时,溢出条件相同。所以你是正确的,溢出条件是相同的。 - Steve Jessop
@SteveJessop 有没有可能指向证明你(被证明正确)的声明的标准呢? - Shmil The Cat
1
@Shmil:我认为我的“证明”仅依赖于2的补码算术的范围:特别是INT_MIN的绝对值比INT_MAX大1(当然,这是在实际数学中计算的绝对值,而不是在整数类型的算术中)。 这就是我声称-x溢出的原因,仅当x为INT_MIN时。毫无疑问,这些限制在当前版本的标准中已经被声明或暗示了,但抱歉,我不会去寻找它 :-) - Steve Jessop

1
x - y的结果被定义为从x中减去y的数学结果。如果数学结果可以表示为结果类型(在这种情况下为int),则没有溢出。

编译器可以任意转换表达式,例如通过更改

x - y

x + (-y)

只有在原有的行为定义良好的情况下,转换保持不变时才会进行。在y == INT_MIN的情况下,只要计算-INT_MIN的未定义行为产生相同的结果,就可以执行转换(通常是这样的)。

回答标题中的问题:

从任何整数中减去INT_MIN是否被认为是未定义行为?

INT_MIN - INT_MIN == 0,不会溢出。

顺便提一下,我认为你的意思是int而不是“integer”。int只是几种整数类型中的一种。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接