C++ 零位移位会正常工作吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/980565/
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 18:13:26  来源:igfitidea点击:

Will bit-shift by zero bits work correctly?

c++bit-manipulationbit-shift

提问by sharptooth

Say I have a function like this:

说我有一个这样的功能:

inline int shift( int what, int bitCount )
{
    return what >> bitCount;
}

It will be called from different sites each time bitCountwill be non-negative and within the number of bits in int. I'm particularly concerned about call with bitCountequal to zero - will it work correctly then?

每次bitCount都会从不同的站点调用它将是非负的并且在int. 我特别担心呼叫bitCount等于零 - 那么它会正常工作吗?

Also is there a chance that a compiler seeing the whole code of the function when compiling its call site will reduce calls with bitCountequal to zero to a no-op?

编译器在编译其调用站点时看到函数的整个代码是否有可能将bitCount等于零的调用减少到无操作?

回答by plinth

According to K&R"The result is undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression's type." (A.7.8) Therefore >> 0is the identity right shift and perfectly legal.

根据 K&R,“如果右操作数为负,或大于或等于左表达式类型中的位数,则结果未定义。” (A.7.8) 因此>> 0是身份右移且完全合法。

回答by Thomas L Holaday

It is certainthat at least one C++ compiler will recognize the situation (when the 0 is known at compile time) and make it a no-op:

可以肯定的是,至少有一个 C++ 编译器会识别这种情况(当 0 在编译时已知时)并使其成为无操作:

Source

来源

inline int shift( int what, int bitcount)
{
  return what >> bitcount ;
}

int f() {
  return shift(42,0);
}

Compiler switches

编译器开关

icpc -S -O3 -mssse3 -fp-model fast=2 bitsh.C

Intel C++ 11.0 assembly

英特尔 C++ 11.0 程序集

# -- Begin  _Z1fv
# mark_begin;
       .align    16,0x90
        .globl _Z1fv
_Z1fv:
..B1.1:                         # Preds ..B1.0
        movl      , %eax                                     #7.10
        ret                                                     #7.10
        .align    16,0x90
                                # LOE
# mark_end;
        .type   _Z1fv,@function
        .size   _Z1fv,.-_Z1fv
        .data
# -- End  _Z1fv
        .data
        .section .note.GNU-stack, ""
# End

As you can see at ..B1.1, Intel compiles "return shift(42,0)" to "return 42."

正如您在 ..B1.1 中看到的,英特尔将“return shift(42,0)”编译为“return 42”。

Intel 11 also culls the shift for these two variations:

英特尔 11 还剔除了这两种变体的转变:

int g() {
  int a = 5;
  int b = 5;
  return shift(42,a-b);
}

int h(int k) {
  return shift(42,k*0);
}

For the case when the shift value is unknowable at compile time ...

对于在编译时不知道移位值的情况......

int egad(int m, int n) {
  return shift(42,m-n);
}

... the shift cannot be avoided ...

……这种转变无法避免……

# -- Begin  _Z4egadii
# mark_begin;
       .align    16,0x90
        .globl _Z4egadii
_Z4egadii:
# parameter 1: 4 + %esp
# parameter 2: 8 + %esp
..B1.1:                         # Preds ..B1.0
        movl      4(%esp), %ecx                                 #20.5
        subl      8(%esp), %ecx                                 #21.21
        movl      , %eax                                     #21.10
        shrl      %cl, %eax                                     #21.10
        ret                                                     #21.10
        .align    16,0x90
                                # LOE
# mark_end;

... but at least it's inlined so there's no call overhead.

...但至少它是内联的,所以没有调用开销。

Bonus assembly: volatile is expensive. The source ...

奖励大会: volatile 是昂贵的。来源 ...

int g() {
  int a = 5;
  volatile int b = 5;
  return shift(42,a-b);
}

... instead of a no-op, compiles to ...

... 而不是空操作,编译为 ...

..B3.1:                         # Preds ..B3.0
        pushl     %esi                                          #10.9
        movl      , (%esp)                                    #12.18
        movl      (%esp), %ecx                                  #13.21
        negl      %ecx                                          #13.21
        addl      , %ecx                                      #13.21
        movl      , %eax                                     #13.10
        shrl      %cl, %eax                                     #13.10
        popl      %ecx                                          #13.10
        ret                                                     #13.10
        .align    16,0x90
                                # LOE
# mark_end;

... so if you're working on a machine where values you push on the stack might not be the same when you pop them, well, this missed optimization is likely the least of your troubles.

...因此,如果您在一台机器上工作,当您弹出它们时,您压入堆栈的值可能不相同,那么这种错过的优化可能是您遇到的最少的麻烦。

回答by Crashworks

It will work correctly on any widely used architecture (I can vouch for x86, PPC, ARM). The compiler will not be able to reduce it to a noop unless the function is inlined.

它可以在任何广泛使用的架构上正常工作(我可以保证 x86、PPC、ARM)。除非函数被内联,否则编译器将无法将其减少为 noop。

回答by jpinto3912

About the correctness of arg << 0 or arg >> 0, no problem, absolutely fine.

关于arg << 0 or arg >> 0的正确性,没问题,绝对没问题。

About the eventual optimizations: This will not be reduced to a >nop< when called with a constant what=0 and/or bitcount=0, unless you declare it as inline and choose optimizations (and your compiler of choice understands what inline is).

关于最终优化:当使用常量 what=0 和/或 bitcount=0 调用时,这不会减少为 >nop<,除非您将其声明为内联并选择优化(并且您选择的编译器了解内联是什么) .

So, bottom line, optimize this code by conditionally calling the function only if the OR of arguments is non zero (about the fastest way I figure to test that both args are non-zero).

因此,最重要的是,仅当参数的 OR 不为零时,才通过有条件地调用函数来优化此代码(这是我认为测试两个参数不为零的最快方法)。

回答by jpinto3912

The compiler could only perform this optimisation do that if it knew at compile time that the bitCount value was zero. That would mean that the passed parameter would have to be a constant:

编译器只有在编译时知道 bitCount 值为零时才能执行此优化。这意味着传递的参数必须是常量:

const int N = 0;
int x = shift( 123, N );

C++ certainly allows such an optimisation to be performed, but I'm not aware of any compilers that do so. The alternative approach the compiler could take:

C++ 当然允许执行这样的优化,但我不知道有任何编译器这样做。编译器可以采用的替代方法:

int x = n == 0 ? 123 : shift( 123, n );

would be a pessimisation in the majority of cases and I can't imagine compiler writer implementing such a thing.

在大多数情况下将是悲观的,我无法想象编译器编写者会实现这样的事情。

Edit:AA shift of zero bits is guaranteed to have no effect on the thing being shifted.

编辑:保证零位的 AA 移位对被移位的事物没有影响。

回答by Dolphin

To make the function somewhat self documenting, you may want to change bitCount to unsigned to signify to callers that a negative value is not valid.

为了使函数具有某种程度的自我记录,您可能需要将 bitCount 更改为 unsigned 以向调用者表示负值无效。