Какой из следующих методов является наилучшим вариантом для деления целого на 2 и почему?
Техника 1:
x = x >> 1;
Техника 2:
x = x / 2;
Здесь x
- целое число.
Какой из следующих методов является наилучшим вариантом для деления целого на 2 и почему?
Техника 1:
x = x >> 1;
Техника 2:
x = x / 2;
Здесь x
- целое число.
Используйте операцию, которая лучше всего описывает то, что вы пытаетесь сделать.
Обратите внимание, что они не совсем эквивалентны. Они могут давать разные результаты для отрицательных целых чисел. Например:
-5 / 2 = -2
-5 >> 1 = -3
Первый выглядит как деление? Нет. Если вы хотите разделить, используйте x / 2
. Компилятор может оптимизировать его для использования бит-сдвига, если это возможно (это называется сокращением силы), что делает его бесполезной микро-оптимизацией, если вы сделаете это самостоятельно.
Чтобы свалить: есть много причин, чтобы использовать x = x / 2;
Вот некоторые из них:
он более четко выражает ваши намерения (предполагая, что вы не имеете дело с битами битов бит или чего-то еще)
компилятор все равно уменьшит это до операции переключения
даже если компилятор не уменьшил его и выбрал более медленную операцию, чем сдвиг, вероятность того, что это в конечном итоге повлияет на производительность вашей программы поддающимся измерению, сама исчезает незначительно (и если она действительно влияет на нее, то у вас есть фактическая причина использовать сдвиг)
если деление будет частью более крупного выражения, вы, скорее всего, получите приоритет правильно, если вы используете оператор деления:
x = x / 2 + 5;
x = x >> 1 + 5; // not the same as above
подписанная арифметика может усложнить ситуацию даже больше, чем упомянутая выше проблема приоритета
чтобы повторить - компилятор уже сделает это для вас в любом случае. Фактически, он преобразует деление на константу в серию сдвигов, добавляет и умножает на все виды чисел, а не только на две. См. этот вопрос для ссылок на дополнительную информацию об этом.
Короче говоря, вы ничего не покупаете, кодируя сдвиг, когда вы действительно хотите умножить или разделить, за исключением, возможно, возросшей возможности введения ошибки. Это была целая жизнь, поскольку компиляторы не были достаточно умны, чтобы оптимизировать такие вещи до смещения, когда это необходимо.
Какой из них является лучшим вариантом и почему для деления целочисленного числа на 2?
Зависит от того, что вы подразумеваете под лучшим.
Если вы хотите, чтобы ваши коллеги ненавидели вас или чтобы ваш код сильно читался, я бы обязательно пошел с первым вариантом.
Если вы хотите разделить число на 2, перейдите со вторым.
Эти два не эквивалентны, они не ведут себя одинаково, если число отрицательное или внутри более крупных выражений - битдвиг имеет более низкий приоритет, чем +
или -
, деление имеет более высокий приоритет.
Вы должны написать свой код, чтобы выразить свои намерения. Если производительность ваша забота, не волнуйтесь, оптимизатор хорошо справляется с такими микро-оптимизациями.
Просто используйте divide (/
), предполагая, что он более ясный. Соответственно будет оптимизирован компилятор.
Я согласен с другими ответами, что вы должны одобрить x / 2
, потому что его намерение более ясное, и компилятор должен оптимизировать его для вас.
Однако другой причиной предпочтительности x / 2
over x >> 1
является то, что поведение >>
зависит от реализации, если x
является подписанным int
и является отрицательным.
Из раздела 6.5.7, пуля 5 стандарта ISO C99:
Результатом
E1 >> E2
являетсяE1
смещение по правому краюE2
. ЕслиE1
имеет беззнаковый тип или еслиE1
имеет подписанный тип и неотрицательное значение, значение результата является неотъемлемой частью фактораE1
/ 2E2
. ЕслиE1
имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.
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
.)
Кнут сказал:
Преждевременная оптимизация - это корень всего зла.
Поэтому я предлагаю использовать x /= 2;
Таким образом, код легко понять, и я думаю, что оптимизация этой операции в этой форме не означает большой разницы для процессора.
Посмотрите на вывод компилятора, который поможет вам решить. Я проверил этот тест на 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
Просто добавленное примечание -
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 с, тогда как x → 1 заняло около 2,6 с для той же программы.
Я бы сказал, что есть несколько вещей, которые следует учитывать.
Bitshift должен быть быстрее, поскольку никакие специальные вычисления на самом деле необходимо было сдвинуть бит, однако, как указано, есть потенциальные проблемы с отрицательным числом. Если у вас есть положительные числа, и ищут скорость, тогда я бы порекомендовал Bitshift.
Оператор разделения очень прост для чтения людьми. Поэтому, если вы ищете читаемость кода, вы можете использовать это. Заметка что поле оптимизации компилятора прошло долгий путь, поэтому упростить код читать и понимать - это хорошая практика.
Если вы после чистой производительности, я бы рекомендовал создать несколько тестов, которые могли бы выполнять операции миллионы раз. Попробуйте выполнить несколько раз (размер выборки), чтобы определить, какой из них статистически лучше всего подходит для вашей OS/Hardware/Compiler/Code.
Что касается процессора, операции бит-сдвига быстрее операций деления.
Однако компилятор знает об этом и будет оптимизирован соответствующим образом в той мере, в какой это возможно,
так что вы можете кодировать таким образом, чтобы максимально удобно и спокойно, зная, что ваш код
работает эффективно. Но помните, что unsigned int
может (в некоторых случаях) оптимизироваться лучше, чем int
по ранее указанным причинам.
Если вам не нужна подписанная арифметика, тогда не включайте знаковый бит.
x = x/2; это подходящий код для использования. но операция зависит от вашей собственной программы того, как вы хотите произвести вывод.
Сделайте ваши намерения более ясными... например, если вы хотите разделить, используйте x/2, и пусть компилятор оптимизирует его для переключения оператора (или чего-то еще).
Сегодня процессоры не позволят этим оптимизации влиять на производительность ваших программ.
Ответ на это будет зависеть от среды, в которой вы работаете.
x /= 2
в x >>= 1
, наличие деления символ будет поднимать больше бровей в этой среде, чем использовать сдвиг, чтобы произвести деление.x >>= 1
с комментарием, объясняющим его аргументацию, вероятно, лучше всего для ясности цели.x /= 2
. Лучше сохранить следующего программиста, который, случается, смотрит на ваш код 10-секундного двойного действия на вашу операцию переключения, чем без необходимости доказывать, что вы знали, что смена была более эффективной без оптимизации компилятора.Все они предполагают целые числа без знака. Простой сдвиг, вероятно, не тот, который вы хотите для подписания. Кроме того, DanielH очень хорошо разбирается в использовании x *= 0.5
для определенных языков, таких как ActionScript.
mod 2, test for = 1. dunno синтаксис в c. но это может быть самым быстрым.
общий правый сдвиг делится:
q = i >> n; is the same as: q = i / 2**n;
это иногда используется для ускорения программ за счет ясности. Я не думаю, что ты должен это сделать. Компилятор достаточно умен, чтобы автоматически выполнять ускорение. Это означает, что перенос сдвига не дает вам ничего за счет ясности.
Взгляните на эту страницу из Практического программирования на C++.
Очевидно, что если вы пишете свой код для следующего парня, который его читает, идите за ясность "x/2".
Однако, если скорость - ваша цель, попробуйте в обоих направлениях и времени результаты. Несколько месяцев назад я работал над растровой схемой свертки, которая включала в себя переход через массив целых чисел и деление каждого элемента 2. Я сделал все возможное, чтобы оптимизировать его, включая старый трюк подстановки "x → 1" для "x/2".
Когда я действительно приурочил оба способа, я с удивлением обнаружил, что x/2 быстрее, чем x → 1
Это использовало Microsoft VS2008 С++ с включенной оптимизацией по умолчанию.
С точки зрения производительности. Операции смены процессора значительно быстрее, чем деактивировать op-коды. Таким образом, разделение на два или умножение на 2 и т.д. Все выгоды от операций смены.
Что касается внешнего вида. Как инженеры, когда мы стали настолько привязаны к косметике, что даже прекрасные дамы не используют!:)
X/Y - правильный оператор... и " → ". Если мы хотим, чтобы два разделили целое число, мы можем использовать (/) оператор дивидендов. оператор сдвига используется для сдвига бит.
х = х /2; х/= 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;
}
}