Округление до следующей степени 2

Я хочу написать функцию, которая возвращает ближайшее ближайшее число в степени 2. Например, если мой ввод - 789, то на выходе должно быть 1024. Есть ли способ добиться этого без использования каких-либо циклов, а просто с использованием некоторых побитовых операторов?

Есть несколько вопросов, соответствующих этому. Например: stackoverflow.com/questions/364985/…

Yann Droneaud 11.03.2013 15:49

В качестве пояснения, нужна ли вам ближайшая степень 2 (например, 65 даст вам 64, но 100 даст вам 128) или ближайшую из вышеперечисленных (например, 65 даст вам 128, а также 100)?

Kim Reece 21.01.2009 20:45

Смотрите здесь возможные решения: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpP‌ owerOf2Float

Stefan 21.01.2009 20:31

@Nathan Ваша ссылка 8 месяцев позже, чем этот вопрос.

Joseph Quinsey 16.01.2014 09:11

Или конвертируйте в Rust и используйте doc.rust-lang.org/stable/std/… ;-) (тоже должно быть в разделе 3.2 Hackers Delight)

JGFMK 31.08.2020 21:49

Связанная ветка @ Nathan действительно размещена позже, чем здесь, но отвечать Джона Феминеллы в этой ветке великолепен. Читатели могут захотеть взглянуть.

Paul Razvan Berg 25.03.2021 22:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
212
7
202 414
26
Перейти к ответу Данный вопрос помечен как решенный

Ответы 26

Ответ принят как подходящий

Проверьте 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 05.10.2013 01:57

@Simon: это портативное решение. Нет единого эффективного алгоритма для всех архитектур

phuclv 06.12.2013 13:27

Что, если само число является степенью двойки?

Litherum 19.12.2013 23:34

@Litherum вы читали коды на бит-твидл-хаках? Случай со степенью двойки уже рассматривался специально

phuclv 29.12.2013 05:42

@rishta оператор ^ в C - это не власть. И это самое медленное решение

phuclv 23.06.2014 08:04

Интернет-архив спешит на помощь: web.archive.org/web/20160703165415/https://… Это все еще ленивый ответ, поэтому я проголосовал против.

Ray 04.07.2016 20:31

Почему 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

Marecky 02.01.2018 03:40

Есть ли шанс получить макро-версию этого?

portforwardpodcast 12.09.2018 07:54

На эту ветку все еще есть ссылки, но этот ответ (и большинство других) сильно устарел. У процессоров есть инструкция, которая может помочь в этом (фактически уже в то время?). От: 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 13.03.2019 14:13

@MappaM Этот ответ по-прежнему актуален и является лучшим портативным способом сделать это. Ваша 64-разрядная версия имеет неопределенное поведение, если x > UINT32_MAX и не является автономной. Кроме того, GCC и Clang используют -mtune=generic по умолчанию (как и большинство дистрибутивов), поэтому ваш код НЕ будет расширяться до инструкции lzcnt на x86_64 - он фактически расширится до чего-то НАМНОГО медленнее (процедура libgcc), если вы не используете что-то вроде -march=native . Таким образом, предложенная вами замена непереносима, содержит ошибки и (обычно) медленнее.

Craig Barnes 07.07.2019 17:34

Я не говорю, что это полный и окончательный ответ, иначе я бы не ответил в комментариях. Но было бы неплохо учесть скорость. На ubuntu он отлично работает по умолчанию. Обратите внимание на _clzl и _clz, так что он работает и для> UINT32_MAX.

MappaM 09.07.2019 12:11

@MappaM звучит как случай «работает на моей машине». Для меня next_pow2(1ULL << 34) возвращает 0, и причина не имеет ничего общего с _clzl против _clz. Хотя вашу функцию легко исправить, я пытаюсь сформулировать простую мысль: если вы собираетесь называть чужой код «сильно устаревшим», вы должны сначала убедиться, что ваш собственный код протестирован и работает, а не просто глючная копипаста.

Craig Barnes 01.12.2019 12:13

просто малейшее замечание. Нет необходимости использовать пост инкремент, декремент.

E_g 01.04.2020 12:05

@florin Мне кажется, что было бы точнее процитировать здесь Bit Twiddling Hacks и сказать, что необходимо найти «результат, который может быть выражен формулой 1U << (lg (v - 1) + 1)», который равен pow (2, (lg (v - 1) + 1). Поскольку фрагмент в ответе на самом деле не вычисляет логарифм по основанию 2, это делает.

haykoandri 13.04.2020 22:07

Уэйти-минти, а что, если v - это единство? Думаю, этому раствору нужен охранник: if ( v == 1 ) return 2;

Ant_222 22.07.2020 02:13

@ Ant_222, но 1 - это степень двойки, это просто нулевая степень, то есть pow(2, 0) == 1. специальный корпус это неверно в общем случае, даже если это может быть полезно для вашего варианта использования

Sam Mason 20.08.2020 10:34
next = pow(2, ceil(log(x)/log(2)));

Это работает путем нахождения числа, на которое вы должны возвести 2, чтобы получить x (возьмите журнал числа и разделите его на журнал желаемой базы, см. Википедию для получения дополнительной информации). Затем округлите это значение с помощью ceil, чтобы получить степень ближайшего целого числа.

Это более универсальный (т.е. более медленный!) Метод, чем побитовые методы, связанные в другом месте, но хорошо знать математику, а?

Начиная с C99, вы также можете просто использовать log2, если это поддерживается вашими инструментами. GCC и VS, похоже, не знают :(

Matthew Read 22.01.2012 09:49

Вам не хватает скобки ... next = pow (2, ceil (log (x) / log (2)));

Matthieu Cormier 17.08.2012 21:10

Однако будьте осторожны с точностью поплавка. log(pow(2,29))/log(2) = 29.000000000000004, поэтому результат будет 230 вместо возврата 229. Думаю, поэтому существуют функции log2?

endolith 09.12.2013 06:52

Быстрее powf(2, ceilf(log2f(val)))

tjklemz 07.01.2014 00:46

Стоимость этого, вероятно, составляет не менее 200 циклов, и это даже не правильно. Почему так много положительных отзывов?

Axel Gneiting 18.08.2015 23:17

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

BlackJack 02.05.2017 19:02

Пожалуйста, обновите ответ и используйте log2 (). Я совершил ошибку, не прочитав комментарии, и потерпел большое поражение в соревновании по CP. :(

Abhishek 12.04.2020 19:15

Может комбинировать логарифмы с битовыми операторами: 1 << ceil (log2 (x))

Matt Eding 13.04.2020 02:34
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;

}

Было бы неплохо, если бы вы это приписали (если только вы не обнаружили). Это взято со страницы взломов.

florin 21.01.2009 20:47

Это для 32-битного числа? Расширение для 64-битной версии?

Jonathan Leffler 21.01.2009 20:52

Джонатан, вам нужно сделать это для верхней половины, и если это ноль, вы сделаете это для нижней половины.

florin 21.01.2009 21:03

@florin, если v - 64-битный тип, не могли бы вы просто добавить "c | = v >> 32" после одного для 16?

Evan Teran 21.01.2009 21:18

Ага - это из-за хитростей, как заметил Флорин.

Eclipse 21.01.2009 22:29

Будьте осторожны при использовании подписанного int. Значения> 0x4000_0000_0000_0000 вернут 0x8000_0000_0000_0000.

Nathan 19.10.2012 03:25

Входные значения> 0x8000_0000_0000_0000 вернут 0. Ввод 0 вернет 0.

Nathan 19.10.2012 03:26

@Nathan printf("%lx\n", upper_power_of_two(0x8000000000000000)); -> 8000000000000000, когда upper_power_of_two() использует 64-битный unsigned long.

chux - Reinstate Monica 26.10.2015 20:29

Я нашел какое-то приложение в JDK ArrayDeque, но у него нет первого оператора v--. Я тестировал несколько случаев, кажется, все в порядке, не уверен, в чем разница

zinking 30.08.2016 11:21

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

Ryan1729 18.04.2018 06:43

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

Craig Barnes 22.09.2018 00:38

Если вам это нужно для вещей, связанных с 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;
    }
}

Флорин: это так. и здесь он используется как петля, не так ли?

Tamas Czinege 21.01.2009 20:56

DrJokepu - я думаю, что Флорин хотел сказать здесь, что OP запросил решение без петель

Eli Bendersky 08.11.2009 08:56

Для поплавков 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, и в зависимости от вашего компилятора может быть доступна встроенная функция, которая сделает эту работу за вас.

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

Naveen 21.01.2009 21:29

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

Jasper Bekkers 21.01.2009 21:32

Это нарушает строгие правила псевдонима. На некоторых компиляторах это может не работать или выдавать предупреждение.

martinkunev 01.12.2017 19:54

Если вы используете 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.

Guillaume 28.07.2013 22:50

Вы можете использовать _BitScanForward на Visual C++

KindDragon 05.12.2013 14:25

Вы также можете использовать __builtin_ctz()

MarkP 31.03.2016 21:21

@MarkP __builtin_ctz() бесполезен для округления любого числа, не являющегося степенью двойки, до следующей степени двойки

Yann Droneaud 01.04.2016 15:42

Пожалуйста, добавьте в свой ответ ссылку на список встроенных побитовых функций для других компиляторов в Википедии: 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)); }

oHo 09.04.2017 05:50

8 бит на байт обычно нормально, но CHAR_BIT более точен.

Gareth A. Lloyd 12.06.2018 15:35

Это выглядит довольно странно и не выглядит портативным, без обид.

user13783520 18.07.2020 05:35

Думаю, это тоже работает:

int power = 1;
while(power < x)
    power*=2;

И ответ - power.

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

Tim MB 17.01.2013 16:44

Это не возвращает ближайшую степень двойки, но ее мощность сразу больше, чем X. Все еще очень хорошо

CoffeDeveloper 09.06.2013 20:34

Вместо умножения можно использовать некую побитовую «магию» power <<= 1

vallentin 30.07.2016 11:01

@Vallentin Это должно быть автоматически оптимизировано компилятором.

MarkWeston 05.10.2017 08:59

Остерегайтесь бесконечного цикла, если x слишком велик (т.е. недостаточно битов для представления следующей степени двойки).

alban 22.03.2019 17:01

Многие архитектуры процессоров поддерживают log base 2 или очень похожую операцию - count leading zeros. Многие компиляторы имеют для этого встроенные функции. См. https://en.wikipedia.org/wiki/Find_first_set

это не о поиске самого высокого установленного бита (= bsr) или подсчете ведущих нулей. он хочет округлять до ближайшей степени 2. ответ с «вычесть 1, затем выполнить bsr и сдвинуть 1 влево» делает это.

Flo 13.10.2018 20:23
/*
** 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, он всегда будет быстрее или с той же скоростью.

xryl669 29.04.2016 17:33

НО ... это может быть оценено препроцессором, если вход является константой, и, следовательно, операция НУЛЯ во время выполнения!

Michael 13.09.2019 19:53

Для полноты, вот реализация с плавающей запятой в стандартном языке 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);
}

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

CoffeDeveloper 26.10.2015 00:36

Случайные браузеры, это будет очень медленно, если у вас нет специализированного оборудования с плавающей запятой. На x86 вы можете обводить этот код кругами, используя бит-тиддлинг. rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl примерно в 25 раз быстрее.

Johan 31.03.2016 18:31

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

мощность двух «этажного» варианта:

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)))

pgplus1628 28.10.2015 16:05

@zorksylar эффективнее бы к if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */

yyny 03.03.2016 19:45

Хорошее решение! но power of two "ceil" option не правильный. Например, когда x = 2, результат должен быть 2 вместо 4.

MZD 18.08.2018 15:02

Для любого беззнакового типа, основанного на 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 07.04.2017 15:47

@martinkunev Просто замените UnsignedType и обработайте его вручную. Я почти уверен, что программист на C сможет расширить этот простой шаблон, игнорируя утверждение std::is_unsigned<UnsignedType>::value.

user877329 01.06.2017 20:53

@ user877329 Конечно, было бы неплохо получить ответ и на Javascript, на всякий случай, если кто-то захочет перевести его на C.

martinkunev 01.06.2017 21:10

@martinkunev UnsignedType в JavaScript? В любом случае, это решение показывает, как это сделать для любого типа UnsignedType, и оно написано на C++, а не в псевдокоде [sizeof (v) * CHAR_BIT вместо чего-то вроде количества бит в объекте UnsignedType].

user877329 01.06.2017 21:46

В 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 вы можете использовать соответствующие встроенные функции.

Бесполезно, но УДИВИТЕЛЬНО!

Marco 19.07.2017 12:12

Предполагая, что у вас есть хороший компилятор, и он может немного покрутить перед рукой, которая сейчас выше меня, но в любом случае это работает !!!

    // 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, но этого не происходит.

0kcats 30.08.2018 21:00

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

NoelC 01.09.2018 02:30

Обновлен ответ, чтобы включить версию, которая возвращает 1 для входного значения 1.

NoelC 01.09.2018 05:53

Адаптированный ответ Пола Диксона на 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

Wolfgang Brehm 28.03.2020 14:30
bit_floor и bit_ceil теперь доступны в C++ 20 из заголовка <bit>. en.cppreference.com/w/cpp/header/bit
SU3 01.12.2020 10:07

Вот что я использую, чтобы это было постоянное выражение, если входные данные являются постоянным выражением.

#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

David Wallace 29.10.2019 21:43
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 содержит свое новое значение после ;, и круглые скобки не меняют этого.

sam hocevar 19.05.2020 15:18

Переносимое решение на C#:

long value = 27
long nextPowerOfTwo = 1 << (int)Math.Ceiling(Math.Log2(value));

nextPowerOfTwo - 32.

Math.Ceiling(Math.Log2(value)) вычисляет экспоненту следующей степени двойки, 1 << вычисляет реальное значение посредством сдвига битов.

Другие вопросы по теме