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

190

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

Нэвин
источник
4
Смотрите здесь для возможных решений: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
Stefan
4
Для пояснения, нужна ли вам ближайшая степень 2 (т. Е. 65 даст вам 64, а 100 даст 128) или ближайшая выше (т. Е. 65 даст 128, а также 100)?
Ким Рис
1
Это несколько вопросов, соответствующих этому. Например: stackoverflow.com/questions/364985/…
Ян Дроно,
7
@ Натан Ваша ссылка на 8 месяцев позже этого вопроса.
Джозеф Куинси

Ответы:

148

Проверьте взломанные бит-хаки . Вам нужно получить основание 2 логарифм, а затем добавить 1 к этому. Пример для 32-битного значения:

Округление до следующей высшей степени 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++;

Расширение на другие значения ширины должно быть очевидным.

флорин
источник
11
Это не самое эффективное решение, потому что многие процессоры имеют специальные инструкции для подсчета лидирующих нулей, которые можно использовать для очень эффективного вычисления log2. См. En.wikipedia.org/wiki/Find_first_set
Саймон
7
@Simon: это портативное решение. Не существует общего эффективного алгоритма для всех архитектур
phuclv
5
Что если само число является степенью двойки?
Litherum
6
На эту тему все еще хорошо ссылаются, но этот ответ (и большинство других) сильно устарел. Процессоры имеют инструкцию, чтобы помочь этому (на самом деле уже в то время?). От: jameshfisher.com/2018/03/30/round-up-power-2.html 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 вместо копирования всех вариантов.
MappaM
2
@MappaM Этот ответ по-прежнему очень актуален и является лучшим портативным способом сделать это. Ваша 64-битная версия имеет неопределенное поведение, если x > UINT32_MAXона не без ветвей. Кроме того, GCC и Clang используют -mtune=genericпо умолчанию (как и большинство дистрибутивов), поэтому ваш код НЕ расширится до lzcntинструкции на x86_64 - он фактически расширится до чего-то НАМНОГО медленнее (подпрограмма libgcc), если вы не используете что-то подобное -march=native. Таким образом, предложенная вами замена непереносима, содержит ошибки и (как правило) медленнее.
Крейг Барнс
76
next = pow(2, ceil(log(x)/log(2)));

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

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

Пол Диксон
источник
3
С C99 вы также можете просто использовать log2, если это поддерживается вашими инструментами. GCC и VS, кажется, не :(
Мэтью Читал
2
Вам не хватает скобки ... next = pow (2, ceil (log (x) / log (2)));
Матье Кормье
13
Будьте осторожны с точностью поплавка. log(pow(2,29))/log(2)= 29.000000000000004, то есть результат 2 30 вместо возврата 2 29. Я думаю, именно поэтому существуют функции log2?
эндолит
48
Стоимость этого, вероятно, составляет не менее 200 циклов, и это даже не правильно. Почему так много голосов?
Аксель Гнайтинг
4
@SuperflyJon Но в нем упоминаются побитовые операторы, и я предполагаю, что правильность подразумевается любым вопросом, если не указано иное.
Блэкджек,
51

Я думаю, что это тоже работает:

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

И ответ есть power.

Хосе Дагоберто
источник
19
Справедливо, вопрос задан без петель. Но как бы ни были умны некоторые другие функции, для кода, не чувствительного к производительности, ответ, который быстро и легко понимается и проверяется на правильность, всегда выигрывает для меня.
Тим М.Б.
2
Это не возвращает ближайшую степень 2, но ее мощность сразу больше, чем X. Все еще очень хорошо
CoffeDeveloper
1
Вместо умножения можно использовать power <<= 1
некоторую побитовую
5
@Vallentin Это должно быть автоматически оптимизировано компилятором.
Марк Уэстон
4
Остерегайтесь бесконечного цикла, если xон слишком велик (т. Е. Битов недостаточно для представления следующей степени 2).
Alban
50
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;

}
Затмение
источник
62
Было бы хорошо, если бы вы приписали это (если вы не обнаружили это). Это происходит со страницы хитов.
Флорина
3
Это для 32-битного числа? Расширение для 64-битного?
Джонатан Леффлер
Джонатан, тебе нужно сделать это для верхней половины, а если это ноль, ты сделай это для нижней половины.
Флорин
5
@florin, если v является 64-битным типом, не могли бы вы просто добавить «c | = v >> 32» после того, как для 16?
Эван Теран
3
Код, который работает только для определенной битовой ширины, должен использовать типы фиксированной ширины вместо типов минимальной ширины. Эта функция должна принимать и возвращать uint32_t.
Крейг Барнс,
36

Если вы используете GCC, возможно, вы захотите взглянуть на Оптимизацию функции next_pow2 () от Lockless Inc .. На этой странице описан способ использования встроенной функции builtin_clz()(счетчик начинается с нуля), а затем используется непосредственно x86 (ia32) команда ассемблера bsr(бит обратной развертки), так же , как это описано в другом ответе «s ссылку на Gamedev сайт . Этот код может быть быстрее, чем те, которые описаны в предыдущем ответе .

Кстати, если вы не собираетесь использовать инструкцию на ассемблере и 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));
}
Ян Дроно
источник
3
Обратите внимание, что это возвращает наименьшую степень 2, превышающую OR, равную x. Изменение (x -1) на x изменяет функцию, возвращая меньшую степень 2, большую, чем x.
Гийом
2
Вы можете использовать _BitScanForwardна Visual C ++
KindDragon
Вы также можете использовать__builtin_ctz()
MarkP
@MarkP __builtin_ctz()будет бесполезно округлять любые числа без степеней 2 до следующей степени двух
Янн Дроно
2
Пожалуйста, добавьте в свой ответ ссылку на Википедию список встроенных побитовых функций для других компиляторов: en.wikipedia.org/wiki/Find_first_set#Tool_and_library_support.                                Пожалуйста, предоставьте также 64-битную версию. Я предлагаю следующую функцию C ++ 11:              constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
olibre
15

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

Мощность двух «напольного» варианта:

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;
}
Влк
источник
2
результат неправильный, если xмощность 2. Требуется микро, чтобы проверить, является ли входной сигнал силой 2. #define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
pgplus1628
@zorksylar более эффективно было быif (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
yyny
Хорошее решение! но power of two "ceil" optionэто не правильно. Например, когда x = 2результат должен быть 2вместо4
МЗД
10

Для любого неподписанного типа, основанного на 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;
}

Там действительно нет цикла, так как компилятор знает во время компиляции количество итераций.

Робсон
источник
4
Обратите внимание, что вопрос о С.
Мартинкунев
@martinkunev Просто замените UnsignedType и обработайте его вручную. Я почти уверен, что программист C может расширить этот простой шаблон, игнорируя std::is_unsigned<UnsignedType>::valueутверждение.
user877329
2
@ user877329 Конечно, было бы неплохо иметь ответ и в Javascript, на тот случай, если кто-то захочет перевести его на C.
martinkunev
@martinkunev UnsignedType в JavaScript? В любом случае, это решение показывает, как это сделать для любого UnsignedType, и оно написано на C ++, а не на псевдокоде [sizeof (v) * CHAR_BIT вместо некоторого числа бит в объекте UnsignedType].
user877329
9

Для поплавков 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
Придумал это после прочтения статьи в википедии о поплавках. Кроме того, я использовал его для вычисления квадратных корней с целочисленной точностью. Также приятно, но еще более не связано.
Джаспер Беккерс
Это нарушает строгие правила наложения имен. На некоторых компиляторах это может не работать или выдавать предупреждение.
Мартынкунев
6

Несмотря на помеченный вопрос, как cздесь мои пять центов. К счастью, C ++ 20 будет включать std::ceil2и std::floor2(см. Здесь ). Это consexprшаблонные функции, текущая реализация GCC использует сдвиг битов и работает с любым целым беззнаковым типом.

kreuzerkrieg
источник
2
Недавно они переименовали его в bit_ceil open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf
Вольфганг Брем
5
/*
** 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. Макрос также полезен для установки константы во время компиляции.

Филип Дж. Фрай
источник
Это, вероятно, худшее решение (также отсутствует суффикс ULL на 64-битной константе). Это будет генерировать 32 теста на вход во всех случаях. Лучше использовать цикл while, он всегда будет быстрее или с той же скоростью.
xryl669
1
НО ... это может быть оценено препроцессором, если входное значение является константой, и, таким образом, операция НОЛЬ во время выполнения!
Майкл
4

Для полноты здесь приведена реализация с плавающей точкой в ​​болотном стандарте 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);
}
doynax
источник
1
Случайные браузеры, если вы читаете этот комментарий, выбирают этот код. Это, несомненно, лучший ответ, никаких специальных инструкций, никаких хитростей, просто эффективный, переносимый и стандартный код. Угадай, почему никто не проголосовал за него ^^
CoffeDeveloper
5
Случайные браузеры, это будет очень медленно, если у вас нет специального оборудования с плавающей запятой. На x86 вы можете бегать по кругу вокруг этого кода, используя битовое чередование. rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,clпримерно в 25 раз быстрее.
Йохан
4

Эффективное 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.

NoelC
источник
Небольшая ошибка: он должен возвращать 1 для 1, но это не так.
0kcats
Спасибо. В приложении, для которого оно было разработано, мы явно нуждались в 2 для первой степени, когда 1 является входной. 1 можно рассматривать как особый случай с условным условием, не генерируя слишком много дополнительных инструкций, которые я себе представляю.
NoelC
Обновлен ответ, чтобы включить версию, которая возвращает 1 для входного значения 1.
NoelC
3

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

Johan
источник
Бесполезно, но УДИВИТЕЛЬНО!
Марко
3

Вот мое решение на C. Надеюсь, это поможет!

int next_power_of_two(int n) {
    int i = 0;
    for (--n; n > 0; n >>= 1) {
        i++;
    }
    return 1 << i;
}
Кевин Ян
источник
0

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

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

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

    // 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
nimig18
источник
0

Я пытаюсь получить ближайшую меньшую степень 2 и сделал эту функцию. Пусть это поможет вам. Просто умножьте ближайший младший номер на 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;
}
Коди Пирсолл
источник
0

Адаптированный ответ Пола Диксона на Excel, это работает отлично.

 =POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))
Тим Тарратт
источник
0

Вариант ответа @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);
}
Олив
источник
0

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

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

будет приятно сводить к константе.

Kaz
источник
0

Следующие разъяснения могут оказаться полезными для вашей цели:

Aashay Sathe
источник
0

Преобразуйте его в число с плавающей точкой, а затем используйте .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

Дэвид Уоллес
источник
Обратите внимание, что этот ответ на питоне
Дэвид Уоллес
0
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
Yossarian42
источник
-1

Если вам это нужно для 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;
    }
}
Пауло Лопес
источник
8
«для» это цикл.
Флорин
1
Флорин: это так. и это используется как цикл здесь, не так ли?
Тамас Чинеге
9
DrJokepu - Я думаю, что Флорин хотел сказать здесь, что ОП попросил решение без петель
Эли Бендерский
-1

Если вы хотите однострочный шаблон. Вот

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содержит новое значение после, ;и скобки не изменяют это.
Сам Хочевар
Более того, это заставляет мои глаза кровоточить.
Донал Феллоуз