当 x=0x80000000,y = 1(32 位补码)时,为什么 `x - y <= x` 为真?

Bra*_*Qin 0 c

我想知道是否x - y溢出。

下面是我的代码。

#include <stdio.h>

/* Determine whether arguments can be subtracted without overflow */
int tsub_ok(int x, int y)
{
    return (y <= 0 && x - y >= x) || (y >= 0 && x - y <= x);
}

int main()
{
    printf("0x80000000 - 1 : %d\n", tsub_ok(0x80000000, 1));
}
Run Code Online (Sandbox Code Playgroud)

为什么我不能得到我期望的结果?

dbu*_*ush 5

您无法通过执行违规操作并查看结果是否回绕来检查有符号整数的溢出。

首先,0x80000000传递给函数的值超出了 32 位的范围int。所以它经历了一个实现定义的转换。在大多数使用 2 的补码的系统上,这将导致具有该表示的值是 -2147483648,这也恰好是 的值INT_MIN

然后您尝试执行x - y这会导致有符号整数溢出,从而触发未定义的行为,给您一个意想不到的结果。

处理这个问题的正确方法是执行一些代数以确保不会发生溢出。

如果xy具有相同的符号,则减法不会溢出。

如果迹象不同并且x是积极的,人们可能会天真地尝试这个:

INT_MAX >= x - y
Run Code Online (Sandbox Code Playgroud)

但这可能会溢出。而是将其更改为数学上等效的:

INT_MAX + y >= x
Run Code Online (Sandbox Code Playgroud)

因为 y 是负数,INT_MAX + y所以不会溢出。

x为负时可以进行类似的检查INT_MIN。全面检查:

if (x>=0 && y>=0) {
    return 1;
} else if (x<=0 && y<=0) {
    return 1;
} else if (x>=0 && INT_MAX + y >= x) {
    return 1;
} else if (x<0 && INT_MIN + y <= x) {
    return 1;
} else {
    return 0;
}
Run Code Online (Sandbox Code Playgroud)