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

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

Техника 1:

x = x >> 1;

Метод 2:

x = x / 2;

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

23 ответов


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

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

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

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

(ideone)


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


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

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

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

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

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

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

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


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

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

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

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

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

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


просто используйте divide (/), предполагая, что это яснее. Компилятор оптимизирует соответственно.


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

однако, еще одна причина для предпочтения x / 2 над x >> 1 это поведение >> - Это зависит от реализации, если x это подпись int и отрицательно.

из раздела 6.5.7, подпункт 5 стандарта ISO C99:

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


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

даже x / 2 не может использовать (медленный) процессор разделения инструкция, потому что некоторые сочетания возможны, но это все равно медленнее, чем x >> 1.

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


взгляните на выходные данные компилятора, чтобы помочь вам решить. Я запустил этот тест на x86-64 с
gcc (GCC) 4.2.1 20070719 [FreeBSD]

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

то, что вы видите, компилятор использует sarl (арифметическая правая смена) инструкция в обоих случаях, поэтому она распознает сходство между двумя выражениями. Если вы используете divide, компилятору также необходимо настроить отрицательные числа. Для этого сдвигает бит знака вниз к биту самого низкого порядка и добавляет Это к результату. Это устраняет проблему off-by-one при сдвиге отрицательных чисел по сравнению с тем, что будет делать разделение.
Поскольку случай деления делает 2 сдвига, в то время как явный случай сдвига делает только один, теперь мы можем объяснить некоторые различия в производительности, измеренные другими ответами здесь.

код C с выходом сборки:

для divide, ваш вход будет

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

и это компилирует к

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

аналогично для shift

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

выход:

    sarl    %edi
    movl    %edi, %eax
    ret

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

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


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


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

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


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

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

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

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

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


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


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


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

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


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

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

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


mod 2, тест для = 1. не знаю синтаксиса в c. но это может быть быстрее всего.


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

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

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

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


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

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

когда я на самом деле приурочен к своему удивлению, я обнаружил, что ... --3-->х/2 был быстрее, чем x>>1

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


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

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


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

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


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

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