Gcc(阿贝尔集团)的大数字加成结果

时间:2012-07-13 18:41:05

标签: c optimization assembly compiler-construction

我正在阅读有关检测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编译器用sum替换x+y
为了弄清楚它是什么,我甚至追踪了程序的汇编代码,但没有替换的迹象。

更新:我的问题的实质是,GCC是否在不计算表达式的情况下评估表达式? 这是 NOT 关于两个补语的问题 您可以在here中看到示例输出。

5 个答案:

答案 0 :(得分:2)

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

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

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

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

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

来自维基百科关于two's complement的文章:

Two's complement    Decimal
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

答案 1 :(得分:0)

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

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

将结果放入另一个临时位置

答案 2 :(得分:0)

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

答案 3 :(得分:0)

如果你的编译器有优化(即-02),那么这个函数就不行了。但是,如果没有优化,我相信这会正常工作。

答案 4 :(得分: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

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

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

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

http://en.wikipedia.org/wiki/FLAGS_register_(computing)

Read flag register from C program