0

我正在阅读有关在 C 中检测溢出的技术。此外,显示检测溢出的错误解决方案的示例之一是:

/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y) {
    int sum = x+y;
    return (sum-x == y) && (sum-y == x);
}

它说它不起作用,因为:

二进制补码加法形成一个阿贝尔群,因此无论加法是否溢出,表达式 (x+y)-x 都将计算为 y,并且 (x+y)-y 将始终计算为 x

它到底是什么意思?这是否意味着C编译器替换sumx+y
为了弄清楚它在说什么,我什至跟踪了程序的汇编代码,但没有替换的迹象。

更新:我的问题的本质是,GCC 是否评估表达式而不计算它?
不是关于二进制补码的问题。您可以在此处
查看示例输出。

4

5 回答 5

2

如果你举一个简单的例子,4 (0b0100) + 5 (0b0101)你可以看到无符号和应该9 (1001)是实际上-7是二进制补码。如果您随后取该总和 (0b1001) 并使用二进制补码算法从中减去 4:

    0b1001 - 0b0100 = 0b1001 + 2s_complement(0b0100) = 0b1001 + 0b1100 = 0b1_0101 

你最终得到 0101,即 5(你在 2 的补码操作期间丢弃了溢出的最重要的 1)。从总和中减去 5 等于 4:

    0b1001 - 0b0101 = 0b1001 + 2s_complement(0b0101) = 0b1001 + 0b1011 = 0b1_0100

这满足您提供的 c 代码,但仍然导致溢出。

来自维基百科关于二进制补码的文章:

二进制补码十进制
0111 7
0110 6
0101 5
0100 4
0011 3
0010 2
0001 1
0000 0
1111 -1
1110 -2
1101 -3
1100 -4
1011 -5
1010 -6
1001 -7
1000 -8

更新:
为了使用我的 INT_MAX = 7 的普通 4 位整数系统演示您的 INT_MAX 示例,我们可以看到与您的 c 代码相同的结果。

    7 + 7 (0b0111 + 0b0111) = 0b1110 (-2 in two's complement)

就像我上面的例子一样,减去,sum - 7将等于7.

    0b1110 - 0b0111 = 0b1110 + 2s_complement(0b0111) = 0b1110 + 0b1001 = 0b1_0111
于 2012-07-13T19:03:57.477 回答
0

关于您发布的代码,它不适用于所有平台,因为某些平台会隐式环绕溢出。该表达式将按以下方式计算:

int x    = 0xFFFFFFF0;
int y    = 0x00000020;

int sum  = x + y;   // value of sum is 0x00000010
                    // would be 0x100000010, but highest order bit is dropped

int diff = sum - y; // value of diff is 0xFFFFFFF0
                    // equal to x

域是封闭的,即溢出将以可预测和可逆的方式环绕。您不能依赖此方法来检查溢出。

也就是说,这种行为是特定于平台的。一些处理器会这样做,而其他处理器会返回最接近的可表示值(即总和将等于 0xFFFFFFFF),在这种情况下,它将按预期工作。

在 C 中检查溢出的最佳方法是使用内联汇编语句将标志压入堆栈,将它们弹出到寄存器中,然后返回值。如果上一次数学运算发生溢出,则设置其中一个标志。我以前这样做过,但不记得怎么做。有关资源和帮助,请参见以下页面:

http://en.wikipedia.org/wiki/FLAGS_register_(计算)

从 C 程序中读取标志寄存器

于 2012-07-13T18:51:30.313 回答
0

我可能错了,但这在我看来就像一个简单的溢出检查......如果 sum 溢出,sum-x 不能等于 y

更多:参数在堆栈中传递给函数, sum 是一个局部变量,它也在堆栈上。编译器可能会将 sum=0, sum+x 和它们 sum+y(堆栈位置 + 另一个堆栈位置)放入 sum 位置。对于 return 语句,它可能会通过复制 sum 变量将结果放入另一个临时位置

于 2012-07-13T18:42:58.733 回答
0

看起来会导致错误结果的错误也会影响后续测试的双方,因此测试永远不会失败。

于 2012-07-13T18:46:36.097 回答
0

如果您的编译器(即-02)有优化,那么这个函数可能不起作用。但是,如果没有优化,我相信这会正常工作。

于 2012-07-13T18:47:08.250 回答