Я хочу написати функцію, яка повертає найближчу наступну потужність 2 числа. Наприклад, якщо мій вхід становить 789, вихід повинен бути 1024. Чи є спосіб досягти цього без використання циклів, а лише з використанням декількох побітових операторів?
Я хочу написати функцію, яка повертає найближчу наступну потужність 2 числа. Наприклад, якщо мій вхід становить 789, вихід повинен бути 1024. Чи є спосіб досягти цього без використання циклів, а лише з використанням декількох побітових операторів?
Відповіді:
Перевірте біт 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++;
Розширення на інші ширини повинно бути очевидним.
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 замість того, щоб копіювати всі параметри навколо.
x > UINT32_MAX
вона не є безгалузевою. Крім того, GCC і Clang використовують -mtune=generic
за замовчуванням (як і більшість дистрибутивів), тому ваш код НЕ розширюватиметься до lzcnt
інструкції на x86_64 - він фактично розшириться на щось набагато повільніше (звичайна програма libgcc), якщо ви не використовуєте щось подібне -march=native
. Тож запропонована вами заміна не переносить, баггі та (як правило) повільніше.
next = pow(2, ceil(log(x)/log(2)));
Це працює, знаходячи число, яке ви мали б підняти на 2, щоб отримати x (візьміть журнал числа та розділіть на журнал потрібної бази, докладніше див. У Вікіпедії ). Потім закруглюйте його вгору за допомогою стелі, щоб отримати найближче ціле число потужності.
Це більш загальний мета (тобто повільніший!) Метод, ніж бітові методи, пов'язані в інших місцях, але добре знати математику, так?
log(pow(2,29))/log(2)
= 29,000000000000004, тож результат 2 30 замість повернення 2 29. Я думаю, що саме тому існують функції log2?
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;
}
uint32_t
.
Я думаю, що це теж працює:
int power = 1;
while(power < x)
power*=2;
І відповідь така power
.
power <<= 1
x
він занадто великий (тобто недостатньо бітів, щоб представити наступну потужність 2).
Якщо ви використовуєте 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));
}
_BitScanForward
на Visual C ++
__builtin_ctz()
__builtin_ctz()
не буде корисним для округлення будь-якої нежитлової кількості 2 до наступної потужності двох
constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Ще один, хоч я і використовую цикл, але це набагато швидше, ніж математичні операнди
потужність двох "поверхових" варіантів:
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)))
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
Для будь-якого неподписаного типу, спираючись на біт 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;
}
Там насправді немає циклу, оскільки компілятор знає кількість ітерацій під час компіляції.
std::is_unsigned<UnsignedType>::value
твердження.
Для плавців 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, і залежно від вашого компілятора може бути наявна робота, яка може виконати роботу за вас.
Незважаючи на питання позначено, як c
тут мої п’ять центів. Пощастило нам, C ++ 20 включить std::ceil2
і std::floor2
(див. Тут ). Це consexpr
шаблонні функції, поточна реалізація GCC використовує бітсіфтинг і працює з будь-яким інтегральним неподписаним типом.
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf
/*
** 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. Макрос також корисний для встановлення константи під час компіляції.
Для повноти тут представлена реалізація з плаваючою комою у болотному стандарті 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);
}
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
приблизно в 25 разів швидше.
Ефективне специфічне рішення 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.
У 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 ви можете використовувати відповідні внутрішні слова.
Багато архітектури процесорів підтримують log base 2
або дуже схожу операцію - count leading zeros
. У багатьох компіляторів є суттєві матеріали для цього. Дивіться https://en.wikipedia.org/wiki/Find_first_set
Якщо припустити, у вас є хороший компілятор, і він може зробити трохи подвійне перед рукою, що вище за мене, на даний момент, але все одно це працює !!!
// 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, щоб отримати найближчу верхню потужність 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;
}
Адаптований відповідь Пола Діксона в 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);
}
Ось що я використовую, щоб це було постійним виразом, якщо вхід є постійним виразом.
#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))
добре зменшиться до постійної.
Перетворіть його у 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
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
Якщо вам це потрібно для матеріалів, пов’язаних із 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;
}
}
Якщо ви хочете однолінійний шаблон. Ось
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)); }
n
декількох разів без точки послідовності недійсне. Ви написали це так, ніби це n-=1
має відбутися спочатку, але єдина гарантія тут полягає в тому, що воно n
містить нове значення після того, як ;
і круглі дужки цього не змінюють.