C++ 哪个更好的选择用于将整数除以 2?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10681375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:20:21  来源:igfitidea点击:

Which is better option to use for dividing an integer number by 2?

c++coptimizationdivisionmicro-optimization

提问by Abhineet

Which of the following techniques is the best option for dividing an integer by 2 and why?

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

Technique 1:

技术1:

x = x >> 1;

Technique 2:

技术2:

x = x / 2;

Here xis an integer.

这里x是一个整数。

回答by Mark Byers

Use the operation that best describes what you are trying to do.

使用最能描述您尝试执行的操作的操作。

  • If you are treating the number as a sequence of bits, use bitshift.
  • If you are treating it as a numerical value, use division.
  • 如果您将数字视为位序列,请使用 bitshift。
  • 如果您将其视为数值,请使用除法。

Note that they are not exactly equivalent. They can give different results for negative integers. For example:

请注意,它们并不完全等效。对于负整数,它们可以给出不同的结果。例如:

-5 / 2  = -2
-5 >> 1 = -3

(ideone)

(ideone)

回答by Cat Plus Plus

Does the first one look like dividing? No. If you want to divide, use x / 2. Compiler can optimise it to use bit-shift if possible (it's called strength reduction), which makes it a useless micro-optimisation if you do it on your own.

第一个看起来像分裂吗?不。如果你想分割,请使用x / 2。如果可能,编译器可以优化它以使用位移(这称为强度降低),如果您自己进行,这将使其成为无用的微优化。

回答by Michael Burr

To pile on: there are so many reasons to favor using x = x / 2;Here are some:

继续:有很多理由喜欢使用x = x / 2;这里有一些:

  • it expresses your intent more clearly (assuming you're not dealing with bit twiddling register bits or something)

  • the compiler will reduce this to a shift operation anyway

  • even if the compiler didn't reduce it and chose a slower operation than the shift, the likelihood that this ends up affecting your program's performance in a measurable way is itself vanishingly small (and if it does affect it measurably, then you have an actual reason to use a shift)

  • if the division is going to be part of a larger expression, you're more likely to get the precedence right if you use the division operator:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
    
  • signed arithmetic might complicate things even more than the precedence problem mentioned above

  • to reiterate - the compiler will already do this for you anyway. In fact, it'll convert division by a constant to a series of shifts, adds, and multiplies for all sorts of numbers, not just powers of two. See this questionfor links to even more information about this.

  • 它更清楚地表达了你的意图(假设你没有处理比特摆弄寄存器位或其他东西)

  • 编译器无论如何都会将其简化为移位操作

  • 即使编译器没有减少它并选择比移位更慢的操作,这最终以可测量的方式影响程序性能的可能性本身也很小(如果它确实对它产生了可测量的影响,那么你有一个实际的使用班次的原因)

  • 如果除法将成为更大表达式的一部分,则如果使用除法运算符,则更有可能获得正确的优先级:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
    
  • 有符号算术可能比上面提到的优先级问题更复杂

  • 重申 - 编译器已经会为你做这件事了。事实上,它会将除以常数转换为一系列移位、加法和乘法,而不仅仅是 2 的幂。有关此问题的更多信息,请参阅此问题的链接。

In short, you buy nothing by coding a shift when you really mean to multiply or divide, except maybe an increased possibility of introducing a bug. It's been a lifetime since compilers weren't smart enough to optimize this kind of thing to a shift when appropriate.

简而言之,当您真正想要乘或除时,您不会通过编码转换来购买任何东西,除非可能增加引入错误的可能性。由于编译器不够聪明,无法在适当的时候将这种事情优化为一种转变,这已经是一辈子了。

回答by Luchian Grigore

Which one is the best option and why for dividing the integer number by 2?

哪一个是最好的选择,为什么要将整数除以 2?

Depends on what you mean by best.

取决于你所说的最好是什么意思。

If you want your colleagues to hate you, or to make your code hard to read, I'd definitely go with the first option.

如果你想让你的同事讨厌你,或者让你的代码难以阅读,我肯定会选择第一个选项。

If you want to divide a number by 2, go with the second one.

如果你想把一个数除以 2,就用第二个。

The two are not equivalent, they don't behave the same if the number is negative or inside larger expressions - bitshift has lower precedence than +or -, division has higher precedence.

两者不等价,如果数字为负数或在更大的表达式中,它们的行为不相同 - bitshift 的优先级低于+or -,除法的优先级更高。

You should write your code to express what its intent is. If performance is your concern, don't worry, the optimizer does a good job at these sort of micro-optimizations.

您应该编写代码来表达其意图。如果您关心性能,请不要担心,优化器在这些微优化方面做得很好。

回答by justin

Just use divide (/), presuming it is clearer. The compiler will optimize accordingly.

只需使用divide ( /),假设它更清楚。编译器会相应地进行优化。

回答by jamesdlin

I agree with other answers that you should favor x / 2because its intent is clearer, and the compiler should optimize it for you.

我同意你应该赞成的其他答案,x / 2因为它的意图更明确,编译器应该为你优化它。

However, another reason for preferring x / 2over x >> 1is that the behavior of >>is implementation-dependent if xis a signed intand is negative.

然而,另一个原因宁愿x / 2x >> 1是的行为>>是实现相关的,如果x是签名int和为负。

From section 6.5.7, bullet 5 of the ISO C99 standard:

来自 ISO C99 标准的第 6.5.7 节第 5 项:

The result of E1 >> E2is E1right-shifted E2bit positions. If E1has an unsigned type or if E1has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1/ 2E2. If E1has a signed type and a negative value, the resulting value is implementation-defined.

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

回答by Thomas Mueller

x / 2is clearer, and x >> 1is not much faster (according to a micro-benchmark, about 30% faster for a Java JVM). As others have noted, for negative numbers the rounding is slightly different, so you have to consider this when you want to process negative numbers. Some compilers may automatically convert x / 2to x >> 1if they know the number can not be negative (even thought I could not verify this).

x / 2更清晰,但并x >> 1没有快多少(根据微基准测试,Java JVM 的速度大约快 30%)。正如其他人所指出的,对于负数,舍入略有不同,因此当您要处理负数时必须考虑这一点。如果某些编译器知道数字不能为负数x / 2x >> 1则它们可能会自动转换为(甚至认为我无法验证这一点)。

Even x / 2may not use the (slow) division CPU instruction, because some shortcuts are possible, but it is still slower than x >> 1.

甚至x / 2可能不使用(慢)除法 CPU 指令,因为一些快捷方式是可能的,但它仍然比x >> 1.

(This is a C / C++ question, other programming languages have more operators. For Java there is also the unsigned right shift, x >>> 1, which is again different. It allows to correctly calculate the mean (average) value of two values, so that (a + b) >>> 1will return the mean value even for very large values of aand b. This is required for example for binary search if the array indices can get very large. There was a bug in many versions of binary search, because they used (a + b) / 2to calculate the average. This doesn't work correctly. The correct solution is to use (a + b) >>> 1instead.)

(这是一个 C / C++ 问题,其他编程语言有更多的运算符。对于 Java 也有无符号右移,x >>> 1,这又是不同的。它允许正确计算两个值的均值(平均值),因此(a + b) >>> 1将即使对于非常大的a和值也返回平均值b。例如,如果数组索引可以变得非常大,则这是二分搜索所必需的。在许多版本的二分搜索中存在一个错误,因为它们用于(a + b) / 2计算平均值。这不会不能正常工作。正确的解决方案是(a + b) >>> 1改用。)

回答by Ivan Californias

Knuth said:

克努特说:

Premature optimization is the root of all evil.

过早的优化是万恶之源。

So I suggest to use x /= 2;

所以我建议使用 x /= 2;

This way the code is easy to understand and also I think that the optimization of this operation in that form, don't mean a big difference for the processor.

这样代码很容易理解,而且我认为以这种形式优化这个操作,对处理器来说并不意味着有很大的不同。

回答by Michael Donohue

Take a look at the compiler output to help you decide. I ran this test on x86-64 with
gcc (GCC) 4.2.1 20070719 [FreeBSD]

查看编译器输出以帮助您做出决定。我用
gcc (GCC) 4.2.1 20070719 [FreeBSD]在 x86-64 上运行了这个测试

Also see compiler outputs online at godbolt.

另请参阅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.

您看到的是编译器sarl在两种情况下都使用了(算术右移)指令,因此它确实识别了两个表达式之间的相似性。如果使用除法,编译器还需要针对负数进行调整。为此,它将符号位向下移动到最低位,并将其添加到结果中。与除法相比,这解决了在移动负数时逐一的问题。
由于除法情况进行了 2 次转换,而显式转换情况仅进行了一次,我们现在可以在这里解释其他答案所衡量的一些性能差异。

C code with assembly output:

带有汇编输出的 C 代码:

For divide, your input would be

对于除法,您的输入将是

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

and this compiles to

这编译为

    movl    %edi, %eax
    shrl    , %eax
    addl    %edi, %eax
    sarl    %eax
    ret

similarly for shift

同样的换档

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

with output:

带输出:

    sarl    %edi
    movl    %edi, %eax
    ret

回答by ansiart

Just an added note -

只是一个补充说明 -

x *= 0.5 will often be faster in some VM-based languages -- notably actionscript, as the variable won't have to be checked for divide by 0.

x *= 0.5 在某些基于 VM 的语言中通常会更快——特别是 actionscript,因为不必检查变量是否被 0 除。