Я хочу написать функцию, которая возвращает ближайшую следующую степень 2 числа. Например, если мой ввод 789, вывод должен быть 1024. Есть ли способ достичь этого без использования циклов, а только с помощью некоторых побитовых операторов?
190
Ответы:
Проверьте взломанные бит-хаки . Вам нужно получить основание 2 логарифм, а затем добавить 1 к этому. Пример для 32-битного значения:
Расширение на другие значения ширины должно быть очевидным.
источник
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
И для 32-битных:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
это если вы используете GCC (и Clang, я думаю?), Но было бы разумно потратить время на найти вызов CLZ вместо копирования всех вариантов.x > UINT32_MAX
она не без ветвей. Кроме того, GCC и Clang используют-mtune=generic
по умолчанию (как и большинство дистрибутивов), поэтому ваш код НЕ расширится доlzcnt
инструкции на x86_64 - он фактически расширится до чего-то НАМНОГО медленнее (подпрограмма libgcc), если вы не используете что-то подобное-march=native
. Таким образом, предложенная вами замена непереносима, содержит ошибки и (как правило) медленнее.Это работает путем нахождения числа, которое вы бы увеличили на 2, чтобы получить x (возьмите логарифм числа и разделите на логарифм нужной базы, см. Википедию ) Затем округлите это до ceil, чтобы получить ближайшее целое число.
Это более общий метод (т. Е. Более медленный!) Метод, чем побитовые методы, связанные в других местах, но полезно знать математику, а?
источник
log(pow(2,29))/log(2)
= 29.000000000000004, то есть результат 2 30 вместо возврата 2 29. Я думаю, именно поэтому существуют функции log2?Я думаю, что это тоже работает:
И ответ есть
power
.источник
power <<= 1
x
он слишком велик (т. Е. Битов недостаточно для представления следующей степени 2).источник
uint32_t
.Если вы используете GCC, возможно, вы захотите взглянуть на Оптимизацию функции next_pow2 () от Lockless Inc .. На этой странице описан способ использования встроенной функции
builtin_clz()
(счетчик начинается с нуля), а затем используется непосредственно x86 (ia32) команда ассемблераbsr
(бит обратной развертки), так же , как это описано в другом ответе «s ссылку на Gamedev сайт . Этот код может быть быстрее, чем те, которые описаны в предыдущем ответе .Кстати, если вы не собираетесь использовать инструкцию на ассемблере и 64-битный тип данных, вы можете использовать это
источник
_BitScanForward
на Visual C ++__builtin_ctz()
__builtin_ctz()
будет бесполезно округлять любые числа без степеней 2 до следующей степени двухconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Еще один, хотя я использую цикл, но это гораздо быстрее, чем математические операнды
Мощность двух «напольного» варианта:
Мощность двух «потолочных» вариантов:
ОБНОВИТЬ
Как упоминалось в комментариях, была ошибка в том, что
ceil
его результат был неправильным.Вот полные функции:
источник
x
мощность 2. Требуется микро, чтобы проверить, является ли входной сигнал силой 2.#define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
power of two "ceil" option
это не правильно. Например, когдаx = 2
результат должен быть2
вместо4
Для любого неподписанного типа, основанного на Bit Twiddling Hacks:
Там действительно нет цикла, так как компилятор знает во время компиляции количество итераций.
источник
std::is_unsigned<UnsignedType>::value
утверждение.Для поплавков IEEE вы сможете сделать что-то подобное.
Если вам нужно целочисленное решение и вы можете использовать встроенную сборку, BSR выдаст вам log2 целого числа на x86. Он подсчитывает, сколько правильных битов установлено, что в точности равно log2 этого числа. Другие процессоры имеют аналогичные инструкции (часто), такие как CLZ, и в зависимости от вашего компилятора может быть встроенная функция, которая сделает всю работу за вас.
источник
Несмотря на помеченный вопрос, как
c
здесь мои пять центов. К счастью, C ++ 20 будет включатьstd::ceil2
иstd::floor2
(см. Здесь ). Этоconsexpr
шаблонные функции, текущая реализация GCC использует сдвиг битов и работает с любым целым беззнаковым типом.источник
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfЕсли вы не хотите рисковать в сфере неопределенного поведения, входное значение должно быть между 1 и 2 ^ 63. Макрос также полезен для установки константы во время компиляции.
источник
Для полноты здесь приведена реализация с плавающей точкой в болотном стандарте C.
источник
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
примерно в 25 раз быстрее.Эффективное Microsoft (например, Visual Studio 2017) специальное решение на C / C ++ для целочисленного ввода. Обрабатывает случай ввода, точно совпадающего со степенью двойки, уменьшая его перед проверкой местоположения старшего значащего 1 бита.
В результате получается около 5 встроенных инструкций для процессора Intel, аналогичных приведенным ниже:
По-видимому, компилятор Visual Studio C ++ не предназначен для оптимизации этого для значений времени компиляции, но он не такой, как там много инструкций.
Редактировать:
Если вы хотите, чтобы входное значение 1 приводило к 1 (2 к нулевой степени), небольшая модификация вышеприведенного кода по-прежнему генерирует прямые инструкции без ветвления.
Создает еще несколько инструкций. Хитрость в том, что Index может быть заменен тестом с последующей инструкцией cmove.
источник
В x86 вы можете использовать инструкции по обработке битов sse4, чтобы сделать это быстро.
В c вы можете использовать соответствующие встроенные функции.
источник
Вот мое решение на C. Надеюсь, это поможет!
источник
Поддержка многих процессорных архитектур
log base 2
или очень похожая работаcount leading zeros
. Многие компиляторы имеют встроенные функции для этого. Смотрите https://en.wikipedia.org/wiki/Find_first_setисточник
Предполагая, что у вас есть хороший компилятор, и он может немного крутиться перед рукой, которая выше меня на данный момент, но в любом случае это работает !!!
Тестовый код ниже:
Выходы:
источник
Я пытаюсь получить ближайшую меньшую степень 2 и сделал эту функцию. Пусть это поможет вам. Просто умножьте ближайший младший номер на 2, чтобы получить ближайшую верхнюю степень 2
источник
Адаптированный ответ Пола Диксона на Excel, это работает отлично.
источник
Вариант ответа @YannDroneaud, действительный
x==1
только для платформ x86, компиляторов, gcc или clang:источник
Вот то, что я использую, чтобы это было постоянное выражение, если входные данные являются постоянным выражением.
Так, например, выражение вроде:
будет приятно сводить к константе.
источник
Следующие разъяснения могут оказаться полезными для вашей цели:
источник
Преобразуйте его в число с плавающей точкой, а затем используйте .hex (), который показывает нормализованное представление IEEE.
>>> float(789).hex() '0x1.8a80000000000p+9'
Затем просто извлеките показатель степени и добавьте 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
И поднять 2 до этой силы.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
источник
источник
Если вам это нужно для OpenGL:
источник
Если вы хотите однострочный шаблон. Вот
или
источник
n
Многократное изменение без точки последовательности недопустимо. Вы написали это так, как будто этоn-=1
должно произойти в первую очередь, но единственная гарантия здесь заключается в том, что оноn
содержит новое значение после,;
и скобки не изменяют это.