Какой вариант лучше использовать для деления целочисленного числа на 2?

Какой из следующих методов является наилучшим вариантом для деления целого на 2 и почему?

Техника 1:

x = x >> 1;

Техника 2:

x = x / 2;

Здесь x - целое число.

Ответ 1

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

  • Если вы обрабатываете число как последовательность бит, используйте бит-брейк.
  • Если вы рассматриваете его как числовое значение, используйте деление.

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

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

(идеон)

Ответ 2

Первый выглядит как деление? Нет. Если вы хотите разделить, используйте x / 2. Компилятор может оптимизировать его для использования бит-сдвига, если это возможно (это называется сокращением силы), что делает его бесполезной микро-оптимизацией, если вы сделаете это самостоятельно.

Ответ 3

Чтобы свалить: есть много причин, чтобы использовать x = x / 2; Вот некоторые из них:

  • он более четко выражает ваши намерения (предполагая, что вы не имеете дело с битами битов бит или чего-то еще)

  • компилятор все равно уменьшит это до операции переключения

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

  • если деление будет частью более крупного выражения, вы, скорее всего, получите приоритет правильно, если вы используете оператор деления:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
    
  • подписанная арифметика может усложнить ситуацию даже больше, чем упомянутая выше проблема приоритета

  • чтобы повторить - компилятор уже сделает это для вас в любом случае. Фактически, он преобразует деление на константу в серию сдвигов, добавляет и умножает на все виды чисел, а не только на две. См. этот вопрос для ссылок на дополнительную информацию об этом.

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

Ответ 4

Какой из них является лучшим вариантом и почему для деления целочисленного числа на 2?

Зависит от того, что вы подразумеваете под лучшим.

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

Если вы хотите разделить число на 2, перейдите со вторым.

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

Вы должны написать свой код, чтобы выразить свои намерения. Если производительность ваша забота, не волнуйтесь, оптимизатор хорошо справляется с такими микро-оптимизациями.

Ответ 5

Просто используйте divide (/), предполагая, что он более ясный. Соответственно будет оптимизирован компилятор.

Ответ 6

Я согласен с другими ответами, что вы должны одобрить x / 2, потому что его намерение более ясное, и компилятор должен оптимизировать его для вас.

Однако другой причиной предпочтительности x / 2 over x >> 1 является то, что поведение >> зависит от реализации, если x является подписанным int и является отрицательным.

Из раздела 6.5.7, пуля 5 стандарта ISO C99:

Результатом E1 >> E2 является E1 смещение по правому краю E2. Если E1 имеет беззнаковый тип или если E1 имеет подписанный тип и неотрицательное значение, значение результата является неотъемлемой частью фактора E1/ 2 E2. Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.

Ответ 7

x / 2 более ясный, а x >> 1 не намного быстрее (согласно микро-бенчмарку, примерно на 30% быстрее для Java JVM). Как отмечали другие, для отрицательных чисел округление немного отличается, поэтому вы должны учитывать это, когда хотите обрабатывать отрицательные числа. Некоторые компиляторы могут автоматически преобразовывать x / 2 в x >> 1, если они знают, что число не может быть отрицательным (даже я не мог проверить это).

Даже x / 2 может не использовать инструкцию с медленным разделением CPU, потому что некоторые ярлыки возможны, но она все еще медленнее, чем x >> 1.

(Это вопрос на C/С++, другие языки программирования имеют больше операторов. Для Java также есть сдвиг без знака, x >>> 1, который снова отличается. Он позволяет правильно рассчитать среднее (среднее) значение два значения, так что (a + b) >>> 1 вернет среднее значение даже для очень больших значений a и b. Это требуется, например, для двоичного поиска, если индексы массива могут быть очень большими. Было ошибка во многих версиях бинарного поиска, потому что они использовали (a + b) / 2 для вычисления среднего значения.Это не работает правильно. Правильное решение заключается в использовании (a + b) >>> 1.)

Ответ 8

Кнут сказал:

Преждевременная оптимизация - это корень всего зла.

Поэтому я предлагаю использовать x /= 2;

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

Ответ 9

Посмотрите на вывод компилятора, который поможет вам решить. Я проверил этот тест на x86-64 с помощью gcc (GCC) 4.2.1 20070719 [FreeBSD]

Также см. компилятор выводит онлайн в godbolt.

Вы видите, что в обоих случаях компилятор использует инструкцию sarl (арифметический сдвиг вправо), поэтому он распознает сходство между этими двумя выражениями. Если вы используете разделение, компилятор также должен настроить отрицательные числа. Для этого он сдвигает бит знака до младшего бита и добавляет его к результату. Это устраняет проблему "один за другим" при смещении отрицательных чисел по сравнению с тем, что будет делать деление.
Так как случай деления имеет 2 смены, тогда как явный случай сдвига только один, мы теперь можем объяснить некоторые различия в производительности, измеренные другими ответами здесь.

Код C со сборкой:

Для деления ваш вход будет

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

и это компилируется в

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

аналогично для сдвига

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

с выходом:

    sarl    %edi
    movl    %edi, %eax
    ret

Ответ 10

Просто добавленное примечание -

x * = 0.5 часто будет быстрее в некоторых языках на основе VM, в частности ActionScript, так как переменная не должна быть проверена для деления на 0.

Ответ 11

Используйте x = x / 2; ИЛИ x /= 2;. Возможно, что в будущем новый программист будет работать над этим. Поэтому ему будет легче узнать, что происходит в строке кода. Все могут не знать о такой оптимизации.

Ответ 12

Я говорю для целей соревнований по программированию. Как правило, они имеют очень большие входы, где деление на 2 происходит много раз, и известно, что ввод положительный или отрицательный.

x → 1 будет лучше, чем x/2. Я проверил на ideone.com, запустив программу, где произошло более 10 ^ 10 делений на 2 операции. x/2 заняло почти 5,5 с, тогда как x → 1 заняло около 2,6 с для той же программы.

Ответ 13

Я бы сказал, что есть несколько вещей, которые следует учитывать.

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

  • Оператор разделения очень прост для чтения людьми.       Поэтому, если вы ищете читаемость кода, вы можете использовать это. Заметка       что поле оптимизации компилятора прошло долгий путь, поэтому упростить код       читать и понимать - это хорошая практика.

  • В зависимости от основного оборудования,       операции могут иметь разные скорости. Amdal закон должен сделать       обычный случай быстро. Таким образом, у вас может быть оборудование, которое может выполнять       разные операции быстрее других. Например, умножая на       0.5 может быть быстрее, чем деление на 2. (Конечно, вам может потребоваться взять слово умножения, если вы хотите применить целочисленное деление).

Если вы после чистой производительности, я бы рекомендовал создать несколько тестов, которые могли бы выполнять операции миллионы раз. Попробуйте выполнить несколько раз (размер выборки), чтобы определить, какой из них статистически лучше всего подходит для вашей OS/Hardware/Compiler/Code.

Ответ 14

Что касается процессора, операции бит-сдвига быстрее операций деления. Однако компилятор знает об этом и будет оптимизирован соответствующим образом в той мере, в какой это возможно, так что вы можете кодировать таким образом, чтобы максимально удобно и спокойно, зная, что ваш код работает эффективно. Но помните, что unsigned int может (в некоторых случаях) оптимизироваться лучше, чем int по ранее указанным причинам. Если вам не нужна подписанная арифметика, тогда не включайте знаковый бит.

Ответ 15

x = x/2; это подходящий код для использования. но операция зависит от вашей собственной программы того, как вы хотите произвести вывод.

Ответ 16

Сделайте ваши намерения более ясными... например, если вы хотите разделить, используйте x/2, и пусть компилятор оптимизирует его для переключения оператора (или чего-то еще).

Сегодня процессоры не позволят этим оптимизации влиять на производительность ваших программ.

Ответ 17

Ответ на это будет зависеть от среды, в которой вы работаете.

  • Если вы работаете с 8-разрядным микроконтроллером или чем-либо без аппаратной поддержки для умножения, ожидается, что смещение битов будет обычным явлением, и хотя компилятор почти наверняка превратит x /= 2 в x >>= 1, наличие деления символ будет поднимать больше бровей в этой среде, чем использовать сдвиг, чтобы произвести деление.
  • Если вы работаете в критичной для производительности среде или разделе кода, или ваш код может быть скомпилирован с оптимизацией компилятора, x >>= 1 с комментарием, объясняющим его аргументацию, вероятно, лучше всего для ясности цели.
  • Если вы не находитесь в одном из вышеуказанных условий, сделайте свой код более удобным для чтения, просто используя x /= 2. Лучше сохранить следующего программиста, который, случается, смотрит на ваш код 10-секундного двойного действия на вашу операцию переключения, чем без необходимости доказывать, что вы знали, что смена была более эффективной без оптимизации компилятора.

Все они предполагают целые числа без знака. Простой сдвиг, вероятно, не тот, который вы хотите для подписания. Кроме того, DanielH очень хорошо разбирается в использовании x *= 0.5 для определенных языков, таких как ActionScript.

Ответ 18

mod 2, test for = 1. dunno синтаксис в c. но это может быть самым быстрым.

Ответ 19

общий правый сдвиг делится:

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

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

Взгляните на эту страницу из Практического программирования на C++.

Ответ 20

Очевидно, что если вы пишете свой код для следующего парня, который его читает, идите за ясность "x/2".

Однако, если скорость - ваша цель, попробуйте в обоих направлениях и времени результаты. Несколько месяцев назад я работал над растровой схемой свертки, которая включала в себя переход через массив целых чисел и деление каждого элемента 2. Я сделал все возможное, чтобы оптимизировать его, включая старый трюк подстановки "x → 1" для "x/2".

Когда я действительно приурочил оба способа, я с удивлением обнаружил, что x/2 быстрее, чем x → 1

Это использовало Microsoft VS2008 С++ с включенной оптимизацией по умолчанию.

Ответ 21

С точки зрения производительности. Операции смены процессора значительно быстрее, чем деактивировать op-коды. Таким образом, разделение на два или умножение на 2 и т.д. Все выгоды от операций смены.

Что касается внешнего вида. Как инженеры, когда мы стали настолько привязаны к косметике, что даже прекрасные дамы не используют!:)

Ответ 22

X/Y - правильный оператор... и " → ". Если мы хотим, чтобы два разделили целое число, мы можем использовать (/) оператор дивидендов. оператор сдвига используется для сдвига бит.

х = х /2; х/= 2; мы можем так использовать.

Ответ 23

В то время как x → 1 быстрее, чем x/2, правильное использование → при работе с отрицательными значениями немного сложнее. Для этого требуется нечто похожее на следующее:

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}