Закруглення до наступної потужності 2


189

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


4
Дивіться тут можливі рішення: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
Стефан

4
З метою уточнення, чи потрібна вам найближча потужність 2 (тобто 65 дасть вам 64, але 100 дасть вам 128) або найближча вище (тобто 65 дасть вам 128, і так буде 100)?
Кім Різ

1
У них кілька запитань, що відповідають цьому. Наприклад: stackoverflow.com/questions/364985 / ...
Yann Droneaud


7
@Nathan Ваше посилання на 8 місяців пізніше цього питання.
Джозеф Квінсі

Відповіді:


148

Перевірте біт Twiddling Hacks . Вам потрібно отримати логарифм основи 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

5
Цей потік все ще добре посилається, але ця відповідь (і більшість інших) сильно застаріла. Процесори мають інструкцію, щоб допомогти цьому (насправді вже на той час?). Від: 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 (і я думаю, що Кланг?), Але було б розумно витратити час на знайдіть дзвінок до 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 (візьміть журнал числа та розділіть на журнал потрібної бази, докладніше див. У Вікіпедії ). Потім закруглюйте його вгору за допомогою стелі, щоб отримати найближче ціле число потужності.

Це більш загальний мета (тобто повільніший!) Метод, ніж бітові методи, пов'язані в інших місцях, але добре знати математику, так?


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 циклів, і це навіть не правильно. Чому для цього є стільки результатів?
Axel Gneiting

4
@SuperflyJon Але він згадує побіжно операторів, і я вважаю, що правильність має на увазі будь-яке питання, якщо не зазначено інше.
BlackJack

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
Код, який працює лише для певної ширини бітів, повинен використовувати типи фіксованої ширини замість типів мінімальної ширини. Ця функція повинна приймати і повертати a uint32_t.
Крейг Барнс

50

Я думаю, що це теж працює:

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

І відповідь така power.


19
Досить справедливим, питання не вимагає циклів. Але наскільки розумний, як і деякі інші функції, для коду, який не є чутливим до продуктивності, для мене завжди перемагає відповідь, яка швидко і легко зрозуміла і перевірила, що вона є правильною.
Тім МБ

2
Це не повертає найближчу потужність 2, але сила цього негайно більша, ніж X. І все-таки дуже добре
CoffeDeveloper

1
Замість того, щоб примножувати, замість цього можна використовувати деякі магічні "чари"power <<= 1
валентін

5
@Vallentin Це слід автоматично оптимізувати компілятором.
MarkWeston

4
Остерігайтеся нескінченного циклу, якщо xвін занадто великий (тобто недостатньо бітів, щоб представити наступну потужність 2).
альбан

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, більшу за АБО, рівну x. Зміна (x -1) на x змінює функцію, щоб повернути меншу потужність на 2 більше, ніж x.
Гійом

2
Ви можете використовувати _BitScanForwardна Visual C ++
KindDragon

Ви також можете скористатися__builtin_ctz()
MarkP

@MarkP __builtin_ctz()не буде корисним для округлення будь-якої нежитлової кількості 2 до наступної потужності двох
Yann Droneaud

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
MZD

10

Для будь-якого неподписаного типу, спираючись на біт 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
Зауважимо, що питання стосується C.
martinkunev

@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 використовує бітсіфтинг і працює з будь-яким інтегральним неподписаним типом.


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 тести на кожен вхід у всіх випадках. Краще використовувати цикл "час", він завжди буде швидше або з однаковою швидкістю.
xryl669

1
АЛЕ ... це може оцінити препроцесор, якщо вхід є постійним, а значить, операція ZERO під час виконання!
Майкл

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

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

Створює лише декілька інструкцій. Хитрість полягає в тому, що індекс можна замінити тестом, за яким слідує інструкція cmove.


Невелика помилка: вона повинна повернути 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 ви можете використовувати відповідні внутрішні слова.


Марно, але дивно!
Марко

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

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

добре зменшиться до постійної.


0

Ви можете знайти таке роз’яснення як корисне для вашої мети:


0

Перетворіть його у float, а потім використовуйте .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

-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містить нове значення після того, як ;і круглі дужки цього не змінюють.
sam hocevar

Більш того, це змушує мої очі кровоточити.
Стипендіати Дональда
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.