it-swarm.com.ru

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

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

Техника 1:

x = x >> 1;

Техника 2:

x = x / 2;

Здесь x является целым числом.

398
Abhineet

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

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

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

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

(ideone)

845
Mark Byers

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

224
Cat Plus Plus

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

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

  • компилятор все равно сведет это к операции сдвига

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

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

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

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

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

189
Michael Burr

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

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

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

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

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

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

62
Luchian Grigore

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

58
justin

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

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

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

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

38
jamesdlin

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

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

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

32
Thomas Mueller

Кнут сказал:

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

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

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

22
Ivan Californias

Посмотрите на вывод компилятора, чтобы помочь вам принять решение. Я провел этот тест на 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
19
Michael Donohue

Просто добавленная заметка -

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

15
ansiart

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

13
Imdad

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

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

12
Shashwat Kumar

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

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

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

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

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

12
James Oravec

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

12
tylerl

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

11
Gooner

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

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

11
Atul Kumar

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

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

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

10
gkimsey

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

8
circusdei

в целом правильное смещение делит:

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

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

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

7
Mouna Cheikhna

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

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

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

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

7
Chris Bennet

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

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

4
Farjad

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

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

3
chocolate boy

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

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