Я хочу написать функцию, которая возвращает ближайшее ближайшее число в степени 2. Например, если мой ввод - 789, то на выходе должно быть 1024. Есть ли способ добиться этого без использования каких-либо циклов, а просто с использованием некоторых побитовых операторов?
В качестве пояснения, нужна ли вам ближайшая степень 2 (например, 65 даст вам 64, но 100 даст вам 128) или ближайшую из вышеперечисленных (например, 65 даст вам 128, а также 100)?
Смотрите здесь возможные решения: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpP owerOf2Float
возможный дубликат Учитывая целое число, как мне найти следующую по величине степень двойки с помощью перестановки битов?
@Nathan Ваша ссылка 8 месяцев позже, чем этот вопрос.
Или конвертируйте в Rust и используйте doc.rust-lang.org/stable/std/… ;-) (тоже должно быть в разделе 3.2 Hackers Delight)
Связанная ветка @ Nathan действительно размещена позже, чем здесь, но отвечать Джона Феминеллы в этой ветке великолепен. Читатели могут захотеть взглянуть.





Проверьте Bit Twiddling Хаки. Вам нужно получить логарифм по основанию 2, а затем прибавить к нему 1. Пример для 32-битного значения:
Round up to the next highest power of 2
unsigned int v; // compute the next highest power of 2 of 32-bit v v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++;
Расширение до другой ширины должно быть очевидным.
Это не самое эффективное решение, потому что у многих процессоров есть специальные инструкции для подсчета начальных нулей, которые можно использовать для очень эффективного вычисления log2. См. en.wikipedia.org/wiki/Find_first_set
@Simon: это портативное решение. Нет единого эффективного алгоритма для всех архитектур
Что, если само число является степенью двойки?
@Litherum вы читали коды на бит-твидл-хаках? Случай со степенью двойки уже рассматривался специально
@rishta оператор ^ в C - это не власть. И это самое медленное решение
Интернет-архив спешит на помощь: web.archive.org/web/20160703165415/https://… Это все еще ленивый ответ, поэтому я проголосовал против.
Почему 5 раз повторяется v |= v >>? Я пробовал это с 2 раза, и он также работает, например: v--; console.info(v); v |= v >> 1; console.info(v); v |= v >> 2; console.info(v); v++; Использование последней версии Chrome на Debian 9 64bit
Есть ли шанс получить макро-версию этого?
На эту ветку все еще есть ссылки, но этот ответ (и большинство других) сильно устарел. У процессоров есть инструкция, которая может помочь в этом (фактически уже в то время?). От: jameshfisher.com/2018/03/30/round-up-power-2.htmluint64_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, вместо того, чтобы копировать и вставлять все параметры вокруг.
@MappaM Этот ответ по-прежнему актуален и является лучшим портативным способом сделать это. Ваша 64-разрядная версия имеет неопределенное поведение, если x > UINT32_MAX и не является автономной. Кроме того, GCC и Clang используют -mtune=generic по умолчанию (как и большинство дистрибутивов), поэтому ваш код НЕ будет расширяться до инструкции lzcnt на x86_64 - он фактически расширится до чего-то НАМНОГО медленнее (процедура libgcc), если вы не используете что-то вроде -march=native . Таким образом, предложенная вами замена непереносима, содержит ошибки и (обычно) медленнее.
Я не говорю, что это полный и окончательный ответ, иначе я бы не ответил в комментариях. Но было бы неплохо учесть скорость. На ubuntu он отлично работает по умолчанию. Обратите внимание на _clzl и _clz, так что он работает и для> UINT32_MAX.
@MappaM звучит как случай «работает на моей машине». Для меня next_pow2(1ULL << 34) возвращает 0, и причина не имеет ничего общего с _clzl против _clz. Хотя вашу функцию легко исправить, я пытаюсь сформулировать простую мысль: если вы собираетесь называть чужой код «сильно устаревшим», вы должны сначала убедиться, что ваш собственный код протестирован и работает, а не просто глючная копипаста.
просто малейшее замечание. Нет необходимости использовать пост инкремент, декремент.
@florin Мне кажется, что было бы точнее процитировать здесь Bit Twiddling Hacks и сказать, что необходимо найти «результат, который может быть выражен формулой 1U << (lg (v - 1) + 1)», который равен pow (2, (lg (v - 1) + 1). Поскольку фрагмент в ответе на самом деле не вычисляет логарифм по основанию 2, это делает.
Уэйти-минти, а что, если v - это единство? Думаю, этому раствору нужен охранник: if ( v == 1 ) return 2;
@ Ant_222, но 1 - это степень двойки, это просто нулевая степень, то есть pow(2, 0) == 1. специальный корпус это неверно в общем случае, даже если это может быть полезно для вашего варианта использования
next = pow(2, ceil(log(x)/log(2)));
Это работает путем нахождения числа, на которое вы должны возвести 2, чтобы получить x (возьмите журнал числа и разделите его на журнал желаемой базы, см. Википедию для получения дополнительной информации). Затем округлите это значение с помощью ceil, чтобы получить степень ближайшего целого числа.
Это более универсальный (т.е. более медленный!) Метод, чем побитовые методы, связанные в другом месте, но хорошо знать математику, а?
Начиная с C99, вы также можете просто использовать log2, если это поддерживается вашими инструментами. GCC и VS, похоже, не знают :(
Вам не хватает скобки ... next = pow (2, ceil (log (x) / log (2)));
Однако будьте осторожны с точностью поплавка. log(pow(2,29))/log(2) = 29.000000000000004, поэтому результат будет 230 вместо возврата 229. Думаю, поэтому существуют функции log2?
Быстрее powf(2, ceilf(log2f(val)))
Стоимость этого, вероятно, составляет не менее 200 циклов, и это даже не правильно. Почему так много положительных отзывов?
@SuperflyJon Но здесь упоминаются побитовые операторы, и я предполагаю, что любой вопрос подразумевает правильность, если не указано иное.
Пожалуйста, обновите ответ и используйте log2 (). Я совершил ошибку, не прочитав комментарии, и потерпел большое поражение в соревновании по CP. :(
Может комбинировать логарифмы с битовыми операторами: 1 << ceil (log2 (x))
unsigned long upper_power_of_two(unsigned long v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
Было бы неплохо, если бы вы это приписали (если только вы не обнаружили). Это взято со страницы взломов.
Это для 32-битного числа? Расширение для 64-битной версии?
Джонатан, вам нужно сделать это для верхней половины, и если это ноль, вы сделаете это для нижней половины.
@florin, если v - 64-битный тип, не могли бы вы просто добавить "c | = v >> 32" после одного для 16?
Ага - это из-за хитростей, как заметил Флорин.
Будьте осторожны при использовании подписанного int. Значения> 0x4000_0000_0000_0000 вернут 0x8000_0000_0000_0000.
Входные значения> 0x8000_0000_0000_0000 вернут 0. Ввод 0 вернет 0.
@Nathan printf("%lx\n", upper_power_of_two(0x8000000000000000)); -> 8000000000000000, когда upper_power_of_two() использует 64-битный unsigned long.
Я нашел какое-то приложение в JDK ArrayDeque, но у него нет первого оператора v--. Я тестировал несколько случаев, кажется, все в порядке, не уверен, в чем разница
@zinking Я знаю, что это старый комментарий, но мне было любопытно, и после того, как я попробовал, кажется, что без декремента, если вход имеет степень двойки, тогда выход будет вдвое больше входного, тогда как с декрементом степень двойки возвращается без изменений.
Код, который работает только для определенной разрядности, должен использовать типы с фиксированной шириной вместо типов с минимальной шириной. Эта функция должна принимать и возвращать uint32_t.
Если вам это нужно для вещей, связанных с OpenGL:
/* Compute the nearest power of 2 number that is
* less than or equal to the value passed in.
*/
static GLuint
nearestPower( GLuint value )
{
int i = 1;
if (value == 0) return -1; /* Error! */
for (;;) {
if (value == 1) return i;
else if (value == 3) return i*4;
value >>= 1; i *= 2;
}
}
Флорин: это так. и здесь он используется как петля, не так ли?
DrJokepu - я думаю, что Флорин хотел сказать здесь, что OP запросил решение без петель
Для поплавков IEEE вы могли бы сделать что-то вроде этого.
int next_power_of_two(float a_F){
int f = *(int*)&a_F;
int b = f << 9 != 0; // If we're a power of two this is 0, otherwise this is 1
f >>= 23; // remove factional part of floating point number
f -= 127; // subtract 127 (the bias) from the exponent
// adds one to the exponent if were not a power of two,
// then raises our new exponent to the power of two again.
return (1 << (f + b));
}
Если вам нужно целочисленное решение и вы можете использовать встроенную сборку, BSR предоставит вам log2 целого числа на x86. Он подсчитывает, сколько правых битов установлено, что в точности равно log2 этого числа. Другие процессоры имеют аналогичные инструкции (часто), такие как CLZ, и в зависимости от вашего компилятора может быть доступна встроенная функция, которая сделает эту работу за вас.
Это интересный, хотя и не связанный с вопросом (я хочу округлить только целые числа), попробую это ..
Придумал это после прочтения статьи в Википедии о поплавках. Кроме того, я использовал его для вычисления квадратного корня с целочисленной точностью. Тоже приятно, но еще более несвязанно.
Это нарушает строгие правила псевдонима. На некоторых компиляторах это может не работать или выдавать предупреждение.
Если вы используете GCC, вы можете взглянуть на Оптимизация функции next_pow2 () от Lockless Inc .. На этой странице описан способ использования встроенной функции builtin_clz() (подсчет нуля в начале) и последующего использования непосредственно инструкции ассемблера x86 (ia32) bsr ( битовое сканирование в обратном направлении), как это описано в ссылка на сайт разработчика игрдругой ответ. Этот код может быть быстрее, чем описанный в предыдущий ответ.
Кстати, если вы не собираетесь использовать инструкцию ассемблера и 64-битный тип данных, вы можете использовать этот
/**
* return the smallest power of two value
* greater than x
*
* Input range: [2..2147483648]
* Output range: [2..2147483648]
*
*/
__attribute__ ((const))
static inline uint32_t p2(uint32_t x)
{
#if 0
assert(x > 1);
assert(x <= ((UINT32_MAX/2) + 1));
#endif
return 1 << (32 - __builtin_clz (x - 1));
}
Обратите внимание, что это возвращает наименьшую степень двойки, превышающую ИЛИ, равную x. Изменение (x -1) на x изменяет функцию, возвращая меньшую степень 2, превышающую x.
Вы можете использовать _BitScanForward на Visual C++
Вы также можете использовать __builtin_ctz()
@MarkP __builtin_ctz() бесполезен для округления любого числа, не являющегося степенью двойки, до следующей степени двойки
Пожалуйста, добавьте в свой ответ ссылку на список встроенных побитовых функций для других компиляторов в Википедии: en.wikipedia.org/wiki/Find_first_set#Tool_and_library_suppor t Пожалуйста, предоставьте также 64-битную версию. Предлагаю следующую функцию C++ 11: constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
8 бит на байт обычно нормально, но CHAR_BIT более точен.
Это выглядит довольно странно и не выглядит портативным, без обид.
Думаю, это тоже работает:
int power = 1;
while(power < x)
power*=2;
И ответ - power.
Достаточно справедливо, что вопрос не задавал никаких петель. Но какими бы умными ни были некоторые другие функции, для кода, который не чувствителен к производительности, ответ, который можно быстро и легко понять и проверить на правильность, всегда побеждает.
Это не возвращает ближайшую степень двойки, но ее мощность сразу больше, чем X. Все еще очень хорошо
Вместо умножения можно использовать некую побитовую «магию» power <<= 1
@Vallentin Это должно быть автоматически оптимизировано компилятором.
Остерегайтесь бесконечного цикла, если x слишком велик (т.е. недостаточно битов для представления следующей степени двойки).
Многие архитектуры процессоров поддерживают log base 2 или очень похожую операцию - count leading zeros. Многие компиляторы имеют для этого встроенные функции. См. https://en.wikipedia.org/wiki/Find_first_set
это не о поиске самого высокого установленного бита (= bsr) или подсчете ведущих нулей. он хочет округлять до ближайшей степени 2. ответ с «вычесть 1, затем выполнить bsr и сдвинуть 1 влево» делает это.
/*
** http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*/
#define __LOG2A(s) ((s &0xffffffff00000000) ? (32 +__LOG2B(s >>32)): (__LOG2B(s)))
#define __LOG2B(s) ((s &0xffff0000) ? (16 +__LOG2C(s >>16)): (__LOG2C(s)))
#define __LOG2C(s) ((s &0xff00) ? (8 +__LOG2D(s >>8)) : (__LOG2D(s)))
#define __LOG2D(s) ((s &0xf0) ? (4 +__LOG2E(s >>4)) : (__LOG2E(s)))
#define __LOG2E(s) ((s &0xc) ? (2 +__LOG2F(s >>2)) : (__LOG2F(s)))
#define __LOG2F(s) ((s &0x2) ? (1) : (0))
#define LOG2_UINT64 __LOG2A
#define LOG2_UINT32 __LOG2B
#define LOG2_UINT16 __LOG2C
#define LOG2_UINT8 __LOG2D
static inline uint64_t
next_power_of_2(uint64_t i)
{
#if defined(__GNUC__)
return 1UL <<(1 +(63 -__builtin_clzl(i -1)));
#else
i =i -1;
i =LOG2_UINT64(i);
return 1UL <<(1 +i);
#endif
}
Если вы не хотите углубляться в область неопределенного поведения, входное значение должно быть от 1 до 2 ^ 63. Макрос также полезен для установки константы во время компиляции.
Это, вероятно, худшее решение (в 64-битной константе также отсутствует суффикс ULL). Это сгенерирует 32 теста для каждого входа во всех случаях. Лучше использовать цикл while, он всегда будет быстрее или с той же скоростью.
НО ... это может быть оценено препроцессором, если вход является константой, и, следовательно, операция НУЛЯ во время выполнения!
Для полноты, вот реализация с плавающей запятой в стандартном языке C.
double next_power_of_two(double value) {
int exp;
if (frexp(value, &exp) == 0.5) {
// Omit this case to round precise powers of two up to the *next* power
return value;
}
return ldexp(1.0, exp);
}
Случайные браузеры, если вы читаете этот комментарий, выбирайте этот код. Это явно лучший ответ, без специальных инструкций, без перебора битов, просто эффективный, переносимый и стандартный код. Угадаю, почему никто не проголосовал за это ^^
Случайные браузеры, это будет очень медленно, если у вас нет специализированного оборудования с плавающей запятой. На x86 вы можете обводить этот код кругами, используя бит-тиддлинг. rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl примерно в 25 раз быстрее.
Еще один, хотя я использую цикл, но он намного быстрее математических операндов
мощность двух «этажного» варианта:
int power = 1;
while (x >>= 1) power <<= 1;
мощность двух "потолочных" вариантов:
int power = 2;
x--; // <<-- UPDATED
while (x >>= 1) power <<= 1;
ОБНОВИТЬ
Как упоминалось в комментариях, в ceil была ошибка, когда результат был неправильным.
Вот полные функции:
unsigned power_floor(unsigned x) {
int power = 1;
while (x >>= 1) power <<= 1;
return power;
}
unsigned power_ceil(unsigned x) {
if (x <= 1) return 1;
int power = 2;
x--;
while (x >>= 1) power <<= 1;
return power;
}
результат неверен, если x имеет мощность 2. Требуется микроконтроллер, чтобы проверить, имеет ли вход мощность 2. #define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
@zorksylar эффективнее бы к 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:
#include <climits>
#include <type_traits>
template <typename UnsignedType>
UnsignedType round_up_to_power_of_2(UnsignedType v) {
static_assert(std::is_unsigned<UnsignedType>::value, "Only works for unsigned types");
v--;
for (size_t i = 1; i < sizeof(v) * CHAR_BIT; i *= 2) //Prefer size_t "Warning comparison between signed and unsigned integer"
{
v |= v >> i;
}
return ++v;
}
На самом деле здесь нет цикла, поскольку компилятор знает во время компиляции количество итераций.
Обратите внимание, что вопрос касается C.
@martinkunev Просто замените UnsignedType и обработайте его вручную. Я почти уверен, что программист на C сможет расширить этот простой шаблон, игнорируя утверждение std::is_unsigned<UnsignedType>::value.
@ user877329 Конечно, было бы неплохо получить ответ и на Javascript, на всякий случай, если кто-то захочет перевести его на C.
@martinkunev UnsignedType в JavaScript? В любом случае, это решение показывает, как это сделать для любого типа UnsignedType, и оно написано на C++, а не в псевдокоде [sizeof (v) * CHAR_BIT вместо чего-то вроде количества бит в объекте UnsignedType].
В x86 вы можете использовать инструкции по манипулированию битами sse4, чтобы сделать это быстро.
//assume input is in eax
popcnt edx,eax
lzcnt ecx,eax
cmp edx,1
jle @done //popcnt says its a power of 2, return input unchanged
mov eax,2
shl eax,cl
@done: rep ret
В c вы можете использовать соответствующие встроенные функции.
Бесполезно, но УДИВИТЕЛЬНО!
Предполагая, что у вас есть хороший компилятор, и он может немного покрутить перед рукой, которая сейчас выше меня, но в любом случае это работает !!!
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
#define SH1(v) ((v-1) | ((v-1) >> 1)) // accidently came up w/ this...
#define SH2(v) ((v) | ((v) >> 2))
#define SH4(v) ((v) | ((v) >> 4))
#define SH8(v) ((v) | ((v) >> 8))
#define SH16(v) ((v) | ((v) >> 16))
#define OP(v) (SH16(SH8(SH4(SH2(SH1(v))))))
#define CB0(v) ((v) - (((v) >> 1) & 0x55555555))
#define CB1(v) (((v) & 0x33333333) + (((v) >> 2) & 0x33333333))
#define CB2(v) ((((v) + ((v) >> 4) & 0xF0F0F0F) * 0x1010101) >> 24)
#define CBSET(v) (CB2(CB1(CB0((v)))))
#define FLOG2(v) (CBSET(OP(v)))
Тестовый код ниже:
#include <iostream>
using namespace std;
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
#define SH1(v) ((v-1) | ((v-1) >> 1)) // accidently guess this...
#define SH2(v) ((v) | ((v) >> 2))
#define SH4(v) ((v) | ((v) >> 4))
#define SH8(v) ((v) | ((v) >> 8))
#define SH16(v) ((v) | ((v) >> 16))
#define OP(v) (SH16(SH8(SH4(SH2(SH1(v))))))
#define CB0(v) ((v) - (((v) >> 1) & 0x55555555))
#define CB1(v) (((v) & 0x33333333) + (((v) >> 2) & 0x33333333))
#define CB2(v) ((((v) + ((v) >> 4) & 0xF0F0F0F) * 0x1010101) >> 24)
#define CBSET(v) (CB2(CB1(CB0((v)))))
#define FLOG2(v) (CBSET(OP(v)))
#define SZ4 FLOG2(4)
#define SZ6 FLOG2(6)
#define SZ7 FLOG2(7)
#define SZ8 FLOG2(8)
#define SZ9 FLOG2(9)
#define SZ16 FLOG2(16)
#define SZ17 FLOG2(17)
#define SZ127 FLOG2(127)
#define SZ1023 FLOG2(1023)
#define SZ1024 FLOG2(1024)
#define SZ2_17 FLOG2((1ul << 17)) //
#define SZ_LOG2 FLOG2(SZ)
#define DBG_PRINT(x) do { std::printf("Line:%-4d" " %10s = %-10d\n", __LINE__, #x, x); } while(0);
uint32_t arrTble[FLOG2(63)];
int main(){
int8_t n;
DBG_PRINT(SZ4);
DBG_PRINT(SZ6);
DBG_PRINT(SZ7);
DBG_PRINT(SZ8);
DBG_PRINT(SZ9);
DBG_PRINT(SZ16);
DBG_PRINT(SZ17);
DBG_PRINT(SZ127);
DBG_PRINT(SZ1023);
DBG_PRINT(SZ1024);
DBG_PRINT(SZ2_17);
return(0);
}
Выходы:
Line:39 SZ4 = 2
Line:40 SZ6 = 3
Line:41 SZ7 = 3
Line:42 SZ8 = 3
Line:43 SZ9 = 4
Line:44 SZ16 = 4
Line:45 SZ17 = 5
Line:46 SZ127 = 7
Line:47 SZ1023 = 10
Line:48 SZ1024 = 10
Line:49 SZ2_16 = 17
Я пытаюсь получить ближайшую меньшую мощность 2 и сделал эту функцию. Может быть, это вам поможет. Просто умножьте ближайшее меньшее число на 2, чтобы получить ближайшую верхнюю степень двойки.
int nearest_upper_power(int number){
int temp=number;
while((number&(number-1))!=0){
temp<<=1;
number&=temp;
}
//Here number is closest lower power
number*=2;
return number;
}
Эффективное решение Microsoft (например, Visual Studio 2017) на C / C++ для ввода целых чисел. Обрабатывает случай, когда входное значение точно соответствует степени двойки, путем уменьшения перед проверкой местоположения самого значимого 1 бита.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, Value - 1);
return (1U << (Index + 1));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if defined(WIN64) // The _BitScanReverse64 intrinsic is only available for 64 bit builds because it depends on x64
inline unsigned long long ExpandToPowerOf2(unsigned long long Value)
{
unsigned long Index;
_BitScanReverse64(&Index, Value - 1);
return (1ULL << (Index + 1));
}
#endif
Это генерирует около 5 встроенных инструкций для процессора Intel, подобных приведенным ниже:
dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl
По-видимому, компилятор Visual Studio C++ не закодирован для оптимизации этого для значений времени компиляции, но это не похоже на то, чтобы там было много инструкций.
Редактировать:
Если вы хотите, чтобы входное значение 1 давало 1 (2 в нулевой степени), небольшая модификация приведенного выше кода по-прежнему генерирует прямые инструкции без ветвления.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, --Value);
if (Value == 0)
Index = (unsigned long) -1;
return (1U << (Index + 1));
}
Генерирует еще несколько инструкций. Хитрость в том, что Index можно заменить тестом, за которым следует инструкция cmove.
Небольшая ошибка: он должен возвращать 1 к 1, но этого не происходит.
Спасибо. В приложении, для которого он был разработан, мы явно нуждались в 2 в первой степени, когда вводится 1. 1 можно рассматривать как частный случай с условным выражением, не генерируя слишком много инструкций, как мне кажется.
Обновлен ответ, чтобы включить версию, которая возвращает 1 для входного значения 1.
Адаптированный ответ Пола Диксона на Excel, это отлично работает.
=POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))
Вариант ответа @YannDroneaud, действительный для x==1, только для форм x86, компиляторов, gcc или clang:
__attribute__ ((const))
static inline uint32_t p2(uint32_t x)
{
#if 0
assert(x > 0);
assert(x <= ((UINT32_MAX/2) + 1));
#endif
int clz;
uint32_t xm1 = x-1;
asm(
"lzcnt %1,%0"
:"=r" (clz)
:"rm" (xm1)
:"cc"
);
return 1 << (32 - clz);
}
Вот мое решение на C. Надеюсь, это поможет!
int next_power_of_two(int n) {
int i = 0;
for (--n; n > 0; n >>= 1) {
i++;
}
return 1 << i;
}
Несмотря на то, что вопрос помечен как c, здесь мои пять центов. К счастью, C++ 20 будет включать std::ceil2 и std::floor2 (см. здесь). Это шаблонные функции consexpr, текущий Реализация GCC использует битовый сдвиг и работает с любым целым беззнаковым типом.
Недавно переименовали в bit_ceilopen-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf
bit_floor и bit_ceil теперь доступны в C++ 20 из заголовка <bit>. en.cppreference.com/w/cpp/header/bitВот что я использую, чтобы это было постоянное выражение, если входные данные являются постоянным выражением.
#define uptopow2_0(v) ((v) - 1)
#define uptopow2_1(v) (uptopow2_0(v) | uptopow2_0(v) >> 1)
#define uptopow2_2(v) (uptopow2_1(v) | uptopow2_1(v) >> 2)
#define uptopow2_3(v) (uptopow2_2(v) | uptopow2_2(v) >> 4)
#define uptopow2_4(v) (uptopow2_3(v) | uptopow2_3(v) >> 8)
#define uptopow2_5(v) (uptopow2_4(v) | uptopow2_4(v) >> 16)
#define uptopow2(v) (uptopow2_5(v) + 1) /* this is the one programmer uses */
Так, например, выражение вроде:
uptopow2(sizeof (struct foo))
красиво уменьшится до константы.
Преобразуйте его в число с плавающей запятой, а затем используйте .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
Обратите внимание, что этот ответ находится в python
import sys
def is_power2(x):
return x > 0 and ((x & (x - 1)) == 0)
def find_nearest_power2(x):
if x <= 0:
raise ValueError("invalid input")
if is_power2(x):
return x
else:
bits = get_bits(x)
upper = 1 << (bits)
lower = 1 << (bits - 1)
mid = (upper + lower) // 2
if (x - mid) > 0:
return upper
else:
return lower
def get_bits(x):
"""return number of bits in binary representation"""
if x < 0:
raise ValueError("invalid input: input should be positive integer")
count = 0
while (x != 0):
try:
x = x >> 1
except TypeError as error:
print(error, "input should be of type integer")
sys.exit(1)
count += 1
return count
Если вам нужен однострочный шаблон. Вот
int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>1)>>2)>>4)>>8)>>16); }
или же
int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>(1<<0))>>(1<<1))>>(1<<2))>>(1<<3))>>(1<<4)); }
Это неопределенное поведение в C или C++ и приведет к ошибкам. Модификация n несколько раз без точки последовательности недопустима. Вы написали это так, как будто n-=1 должен произойти первым, но единственная гарантия здесь заключается в том, что n содержит свое новое значение после ;, и круглые скобки не меняют этого.
Переносимое решение на C#:
long value = 27
long nextPowerOfTwo = 1 << (int)Math.Ceiling(Math.Log2(value));
nextPowerOfTwo - 32.
Math.Ceiling(Math.Log2(value)) вычисляет экспоненту следующей степени двойки, 1 << вычисляет реальное значение посредством сдвига битов.
Есть несколько вопросов, соответствующих этому. Например: stackoverflow.com/questions/364985/…