【发布时间】:2015-05-21 12:25:32
【问题描述】:
我想看看 GCC 是否会使用有符号和无符号整数将 a - (b - c) 减少到 (a + c) - b,所以我创建了两个测试
//test1.c
unsigned fooau(unsigned a, unsigned b, unsigned c) { return a - (b - c); }
signed fooas(signed a, signed b, signed c) { return a - (b - c); }
signed fooms(signed a) { return a*a*a*a*a*a; }
unsigned foomu(unsigned a) { return a*a*a*a*a*a; }
//test2.c
unsigned fooau(unsigned a, unsigned b, unsigned c) { return (a + c) - b; }
signed fooas(signed a, signed b, signed c) { return (a + c) - b; }
signed fooms(signed a) { return (a*a*a)*(a*a*a); }
unsigned foomu(unsigned a) { return (a*a*a)*(a*a*a); }
我首先用gcc -O3 test1.c test2.c -S 编译并查看了程序集。对于这两个测试,fooau 相同,但 fooas 不同。
据我所知,无符号算术可以从the following formula推导出来
(a%n + b%n)%n = (a+b)%n
可以用来表明无符号算术是结合的。但由于signed overflow is undefined behavior 这种等式不一定适用于有符号加法(即有符号加法不是关联的),这就解释了为什么 GCC 没有将有符号整数的 a - (b - c) 减少到 (a + c) - b。但是我们可以通过-fwrapv 告诉 GCC 使用这个公式。对两个测试使用此选项fooas 是相同的。
但是乘法呢?对于两个测试,fooms 和 foomu 都被简化为三个乘法 (a*a*a*a*a*a to (a*a*a)*(a*a*a))。但是乘法可以写成重复加法,所以使用上面的公式我认为可以证明
((a%n)*(b%n))%n = (a*b)%n
我认为这也可以表明无符号模乘也是结合的。但由于 GCC 仅对 foomu 使用了三个乘法,这表明 GCC 假设有符号整数乘法是关联的。
这对我来说似乎很矛盾。对于加法,有符号算术不是关联的,但对于乘法,它是关联的。
两个问题:
加法与有符号整数无关,但乘法在 C/C++ 中是真的吗?
如果使用有符号溢出进行优化,GCC 不减少代数表达式是不是优化失败?使用
-fwrapv优化优化不是更好吗(我知道a - (b - c)到(a + c) - b并没有太大的减少,但我担心更复杂的情况)?这是否意味着有时使用-fwrapv进行优化会更有效,有时却不是?
【问题讨论】:
-
如果您将身体设为
a*a*a*a*a,fooms和foomu会发生什么 - 即。 奇数个乘法?他们仍然优化相同吗?对于偶数,符号无关紧要,因为结果总是正数。 -
@kdopen,它是一样的:
fooms和foomu产生相同的代码并对a*a*a*a*a使用 3 次乘法。
标签: c++ c gcc optimization integer