这个问题已经在荒谬的鱼博客上得到了回答:http://ridiculousfish.com/blog/posts/will-it-optimize.html http://ridiculousfish.com/blog/posts/will-it-optimize.html
- 除以 2 右移
GCC 会将整数除以 2 转换为右移吗?
int halve_it(int x) {
return x / 2;
}
int halve_it(int x) {
return x >> 1;
}
右移运算符相当于四舍五入的除法
负无穷大,但普通除法向零舍入。就这样
对于奇数负数,建议的优化将会产生错误的结果
数字。
通过将最高有效位添加到结果中可以“修复”结果
分子在移位之前,gcc 就是这样做的。
优秀的程序员会让编译器优化他们的代码,除非它们会造成性能损失。
EDIT :既然你要求官方来源,我们就引用C99的标准原理文档。你可以在这里找到它:http://www.open-std.org/jtc1/sc22/wg14/www/docs/C99RationaleV5.10.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/C99RationaleV5.10.pdf
在 C89 中,涉及负操作数的整数除法可以以实现定义的方式向上或向下舍入;目的是避免在运行时代码中产生开销来检查特殊情况并强制执行特定行为。然而,在 Fortran 中,结果总是会截断为零,并且开销对于数字编程社区来说似乎是可以接受的。因此,C99 现在需要类似的行为,这应该有助于将代码从 Fortran 移植到 C。本文档第 7.20.6.2 节中的表说明了所需的语义。
您的优化在 C89 中是正确的,因为它让编译器按照自己的意愿行事。然而,C99 引入了新的约定来遵守 Fortran 代码。以下是除法运算符的预期示例(始终来自同一文档):
不幸的是,您的优化不符合 C99 标准,因为它没有给出 x = -1 的正确结果:
#include <stdio.h>
int div8(int x)
{
return x/3;
}
int rs8( int x )
{
return x >> 3;
}
int main(int argc, char *argv[])
{
volatile int x = -1;
printf("div : %d \n", div8(x) );
printf("rs : %d \n", rs8(x) );
return 0;
}
Result:
div : 0
rs : -1
[Finished in 0.2s]
如果查看编译后的代码,您可以发现一个有趣的差异(使用 g++ v4.6.2 编译):
0040138c <__Z4div8i>:
40138c: 55 push %ebp
40138d: 89 e5 mov %esp,%ebp
40138f: 8b 45 08 mov 0x8(%ebp),%eax
401392: 85 c0 test %eax,%eax
401394: 79 03 jns 401399 <__Z4div8i+0xd>
401396: 83 c0 0f add $0x7,%eax
401399: c1 f8 04 sar $0x3,%eax
40139c: 5d pop %ebp
40139d: c3 ret
0040139e <__Z3rs8i>:
40139e: 55 push %ebp
40139f: 89 e5 mov %esp,%ebp
4013a1: 8b 45 08 mov 0x8(%ebp),%eax
4013a4: c1 f8 03 sar $0x3,%eax
4013a7: 5d pop %ebp
4013a8: c3 ret
line 401392
,有一个test
指令,它将检查奇偶校验位,如果数字为负,则添加1 << (n-1) = 7
右移 3 个单位之前的 x。