以下哪一种技术是整数除2的最佳选择,为什么?

技巧1:

x = x >> 1;

技巧2:

x = x / 2;

这里x是整数。


当前回答

我同意其他答案,你应该支持x / 2,因为它的意图更清楚,编译器应该为你优化它。

然而,选择x / 2而不是x >> 1的另一个原因是,如果x是一个有符号int并且是负的,那么>>的行为是依赖于实现的。

ISO C99标准第6.5.7节第5项:

E1 >> E2的结果是E1位右移E2位位置。如果E1 无符号类型,或者E1有符号类型且值为非负值, 结果值为E1 /商的积分部分 2 e2。如果E1具有符号类型和负值,则结果值为 是由实现定义的。

其他回答

使用x = x / 2;或x /= 2;因为将来有可能会有一个新的程序员使用它。因此,他更容易发现代码行中发生了什么。每个人可能都不知道这种优化。

让你的意图更清楚……例如,如果你想除法,使用x / 2,并让编译器将其优化为shift运算符(或其他任何运算符)。

今天的处理器不会让这些优化对程序的性能产生任何影响。

查看编译器的输出来帮助您做出决定。我在x86-64上使用 gcc (gcc) 4.2.1 20070719 [FreeBSD]

也可以在godbolt上看到编译器的在线输出。

What you see is the compiler does use a sarl (arithmetic right-shift) instruction in both cases, so it does recognize the similarity between the two expressions. If you use the divide, the compiler also needs to adjust for negative numbers. To do that it shifts the sign bit down to the lowest order bit, and adds that to the result. This fixes the off-by-one issue when shifting negative numbers, compared to what a divide would do. Since the divide case does 2 shifts, while the explicit shift case only does one, we can now explain some of the performance differences measured by other answers here.

C代码与汇编输出:

对于除法,你的输入是

int div2signed(int a) {
  return a / 2;
}

这个编译成

    movl    %edi, %eax
    shrl    $31, %eax            # (unsigned)x >> 31
    addl    %edi, %eax           # tmp = x + (x<0)
    sarl    %eax                 # (x + 0 or 1) >> 1  arithmetic right shift
    ret

shift也是一样

int shr2signed(int a) {
  return a >> 1;
}

输出:

    sarl    %edi
    movl    %edi, %eax
    ret

其他isa即使不能做得更好,也能同样有效地做到这一点。例如GCC For AArch64使用:

        add     w0, w0, w0, lsr 31      // x += (unsigned)x>>31
        asr     w0, w0, 1               // x >>= 1
        ret

一般右移分为:

q = i >> n; is the same as: q = i / 2**n;

这有时被用来加快程序的速度,但以清晰度为代价。我觉得你不应该这么做。编译器足够智能,可以自动执行加速。这意味着,以清晰度为代价,你不会获得任何好处。

看看《实用c++编程》的这一页。

这个问题的答案取决于你工作的环境。

If you're working on an 8-bit microcontroller or anything without hardware support for multiplication, bit shifting is expected and commonplace, and while the compiler will almost certainly turn x /= 2 into x >>= 1, the presence of a division symbol will raise more eyebrows in that environment than using a shift to effect a division. If you're working in a performance-critical environment or section of code, or your code could be compiled with compiler optimization off, x >>= 1 with a comment explaining its reasoning is probably best just for clarity of purpose. If you're not under one of the above conditions, make your code more readable by simply using x /= 2. Better to save the next programmer who happens to look at your code the 10 second double-take on your shift operation than to needlessly prove you knew the shift was more efficient sans compiler optimization.

所有这些假设都是无符号整数。简单的移位可能不是你想要的符号。此外,DanielH提出了一个关于在ActionScript等特定语言中使用x *= 0.5的好观点。