Будет ли бит-сдвиг нулевыми битами работать правильно?

Скажем, у меня есть такая функция:

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

Он будет вызываться с разных сайтов каждый раз, когда bitCount будет неотрицательным и в пределах числа бит в int. Меня особенно беспокоит вызов с bitCount равным нулю - будет ли он работать правильно?

Также существует вероятность того, что компилятор, просматривающий весь код функции при компиляции своего сайта вызова, уменьшит количество вызовов с bitCount равным нулю для no-op?

Ответы

Ответ 1

Несомненно, что хотя бы один компилятор С++ распознает ситуацию (когда значение 0 известно во время компиляции) и делает его no-op:

Источник

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

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

Переключатели компилятора

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

сборка Intel С++ 11.0

# -- Begin  _Z1fv
# mark_begin;
       .align    16,0x90
        .globl _Z1fv
_Z1fv:
..B1.1:                         # Preds ..B1.0
        movl      $42, %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

Как вы можете видеть на..B1.1, Intel компилирует "return shift (42,0)" в "return 42".

Intel 11 также отбрасывает сдвиг для этих двух вариантов:

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

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

В случае, когда значение сдвига непознаваемо во время компиляции...

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

... сдвига нельзя избежать...

# -- 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      $42, %eax                                     #21.10
        shrl      %cl, %eax                                     #21.10
        ret                                                     #21.10
        .align    16,0x90
                                # LOE
# mark_end;

... но, по крайней мере, он встроен, поэтому нет накладных расходов.

Бонусная сборка: летучая стоит дорого. Источник...

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

... вместо no-op компилируется...

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

... так что если вы работаете на машине, где значения, которые вы нажимаете на стек, могут быть не такими же, когда вы их всплываете, ну, эта пропущенная оптимизация, скорее всего, будет наименее из ваших проблем.

Ответ 2

Согласно K & R "Результат не определен, если правый операнд отрицательный или больше или равен числу битов в левом типе выражения". (A.7.8) Следовательно, >> 0 - это смена прав личности и совершенно законно.

Ответ 3

Он будет корректно работать с любой широко используемой архитектурой (я могу поручиться за x86, PPC, ARM). Компилятор не сможет уменьшить его до noop, если функция не встроена.

Ответ 4

Компилятор мог выполнить эту оптимизацию только тогда, когда во время компиляции было известно, что значение bitCount равно нулю. Это означало бы, что передаваемый параметр должен быть константой:

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

С++, конечно, позволяет выполнить такую ​​оптимизацию, но я не знаю никаких компиляторов, которые это делают. Альтернативный подход, который может использовать компилятор:

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

будет пессимизацией в большинстве случаев, и я не могу представить, чтобы автор компилятора реализовал такую ​​вещь.

Изменить: Смещение AA с нулевыми битами не окажет никакого влияния на перемещаемую вещь.

Ответ 5

О правильности arg < 0 или arg → 0, без проблем, абсолютно нормально.

О возможных оптимизации: Это не будет уменьшено до a > nop < когда вызывается с константой what = 0 и/или bitcount = 0, если вы не объявляете ее как встроенную и не выбираете оптимизацию (и ваш компилятор выбора понимает, что такое строка).

Итак, в нижней строке, оптимизируйте этот код, условно вызывая функцию только в том случае, если OR аргументов не равен нулю (о самом быстром способе, который я считаю для проверки того, что оба аргумента не равны нулю).

Ответ 6

Чтобы сделать процедуру несколько самостоятельной документацией, вы можете изменить bitCount на unsigned, чтобы обозначить вызывающим, что отрицательное значение недействительно.