Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Проверьте бит 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++;

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

Ответ 2

next = pow(2, ceil(log(x)/log(2)));

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

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

Ответ 3

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;

}

Ответ 4

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

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

И ответ power.

Ответ 5

Если вы используете GCC, вы можете взглянуть на Оптимизацию функции next_pow2() Lockless Inc.. На этой странице описывается способ использования встроенной функции builtin_clz() (count leading zero), а затем использовать напрямую x86 (ia32) ассемблерной инструкции bsr (обратное сканирование бит), точно так же, как описано в другой ссылке ответа на сайт 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));
}

Ответ 6

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

мощность двух "этажа":

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

Ответ 7

Для любого неподписанного типа, построенного на бета-битках:

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

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

Ответ 8

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

Ответ 9

/*
** 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
}

Если вы не хотите рисковать в области поведения undefined, входное значение должно быть от 1 до 2 ^ 63. Макрос также полезен для установки константы во время компиляции.

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

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

int next_power_of_two(int n) {
    int i = 0;
    for (--n; n > 0; n >>= 1) {
        i++;
    }
    return 1 << i;
}

Ответ 14

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

Ответ 15

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

Ответ 16

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

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

Ответ 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;
}

Ответ 18

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

 =POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))

Ответ 19

Вариант ответа @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);
}

Ответ 20

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

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

приятно уменьшится до константы.

Ответ 21

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

Ответ 22

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