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


если у меня есть целое число n, Как я могу найти следующее число k > n такое, что k = 2^i, С i элемент N побитового сдвига или логики.

пример: если у меня есть n = 123, как я могу найти k = 128, который является силой двух, а не 124 который делится только на два. Это должно быть просто, но это ускользает от меня.

17 72

17 ответов:

для 32-разрядных целых чисел, это простой и прямой маршрут:

unsigned int n;

n--;
n |= n >> 1;   // Divide by 2^k for consecutive doublings of k up to 32,
n |= n >> 2;   // and then or the results.
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;           // The result is a number of 1 bits equal to the number
               // of bits in the original number, plus 1. That's the
               // next highest power of 2.

вот более конкретный пример. Давайте возьмем число 221, которое является 11011101 в двоичном формате:

n--;           // 1101 1101 --> 1101 1100
n |= n >> 1;   // 1101 1100 | 0110 1110 = 1111 1110
n |= n >> 2;   // 1111 1110 | 0011 1111 = 1111 1111
n |= n >> 4;   // ...
n |= n >> 8;
n |= n >> 16;  // 1111 1111 | 1111 1111 = 1111 1111
n++;           // 1111 1111 --> 1 0000 0000

есть один бит в девятой позиции, который представляет 2^8, или 256, что действительно является следующей по величине силой 2. Каждый из сдвигов перекрывает все существующие 1 бит в числе с некоторыми из ранее нетронутых нулей, в конечном итоге создавая число 1 биты равны количеству битов в исходном числе. Добавление одного к этому значению дает новую степень 2.

другой пример, мы будем использовать 131, который 10000011 в двоичном виде:

n--;           // 1000 0011 --> 1000 0010
n |= n >> 1;   // 1000 0010 | 0100 0001 = 1100 0011
n |= n >> 2;   // 1100 0011 | 0011 0000 = 1111 0011
n |= n >> 4;   // 1111 0011 | 0000 1111 = 1111 1111
n |= n >> 8;   // ... (At this point all bits are 1, so further bitwise-or
n |= n >> 16;  //      operations produce no effect.)
n++;           // 1111 1111 --> 1 0000 0000

и действительно, 256-это следующая высшая степень 2 из 131.

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

для этого есть решение для сборки (начиная с набора инструкций 80386).

вы можете использовать инструкцию BSR (bit Scan Reverse) для сканирования наиболее значимого бита в вашем целочисленном значении.

bsr сканирует биты, начиная с самый значительный бит, в операнд двойного слова или второе слово. Если все биты равны нулю, то ZF равен очищенный. В противном случае ZF устанавливается и битовый индекс первого найденного бита набора, при сканировании в обратном направление, нагружено в регистр назначения

(извлечено из: http://dlc.sun.com/pdf/802-1948/802-1948.pdf)

и чем ВКЛ результат с 1.

так:

bsr ecx, eax  //eax = number
jz  @zero
mov eax, 2    // result set the second bit (instead of a inc ecx)
shl eax, ecx  // and move it ecx times to the left
ret           // result is in eax

@zero:
xor eax, eax
ret

в новых процессорах вы можете использовать гораздо быстрее lzcnt инструкция (ака rep bsr). lzcnt делает свою работу за один цикл.

более математический способ, без петель:

public static int ByLogs(int n)
{
    double y = Math.Floor(Math.Log(n, 2));

    return (int)Math.Pow(2, y + 1);
}

вот логический ответ:

function getK(int n)
{
  int k = 1;
  while (k < n)
    k *= 2;
  return k;
}

здесь Джон Feminella реализованные в виде петли, поэтому она может обрабатывать питон:

def next_power_of_2(n):
    """
    Return next power of 2 greater than or equal to n
    """
    n -= 1 # greater than OR EQUAL TO n
    shift = 1
    while (n+1) & n: # n+1 is not a power of 2 yet
        n |= n >> shift
        shift <<= 1
    return n + 1

Он также возвращается быстрее, если n уже имеет степень 2.

для Python > 2.7, это проще и быстрее для большинства N:

def next_power_of_2(n):
    """
    Return next power of 2 greater than or equal to n
    """
    return 2**(n-1).bit_length()

enter image description here

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

//  compute k = nextpowerof2(n)

if (n > 1) 
{
  float f = (float) n;
  unsigned int const t = 1U << ((*(unsigned int *)&f >> 23) - 0x7f);
  k = t << (t < n);
}
else k = 1;

это, и многие другие бит-twiddling хаки, в том числе на представленный Джон Феминелла, можно найти здесь.

предположим, что x не отрицательно.

int pot = Integer.highestOneBit(x);
if (pot != x) {
    pot *= 2;
}

Если вы используете GCC, MinGW или Clang:

template <typename T>
T nextPow2(T in)
{
  return (in & (T)(in - 1)) ? (1U << (sizeof(T) * 8 - __builtin_clz(in))) : in;
}

Если вы используете Microsoft Visual C++, используйте функцию _BitScanForward() заменить __builtin_clz().

function Pow2Thing(int n)
{
    x = 1;
    while (n>0)
    {
        n/=2;
        x*=2;
    }
    return x;
}

немного крутится, говоришь?

long int pow_2_ceil(long int t) {
    if (t == 0) return 1;
    if (t != (t & -t)) {
        do {
            t -= t & -t;
        } while (t != (t & -t));
        t <<= 1;
    }
    return t;
}

каждый цикл непосредственно удаляет наименее значимый 1-бит. N. B. Это работает только там, где подписанные числа кодируются в дополнение к двум.

больше / больше или равно

следующие фрагменты предназначены для следующее число k > n такое, что k = 2^i
(n=123 = > k = 128, n=128 => k=256), как указано в OP.

если вы хотите наименьшая мощность 2 больше или равна n потом просто заменить __builtin_clzll(n) by __builtin_clzll(n-1) в приведенных выше фрагментах.

C++11 с помощью GCC или Clang (64 бит)

constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
    return 1ULL << (sizeof(uint64_t) * 8 - __builtin_clzll(n));
}

улучшение с помощью CHAR_BIT как предложил martinec

#include <cstdint>

constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
    return 1ULL << (sizeof(uint64_t) * CHAR_BIT - __builtin_clzll(n));
}

C++17 с помощью GCC или Clang (от 8 до 128 бит)

#include <cstdint>

template <typename T>
constexpr T nextPowerOfTwo64 (T n)
{
   T clz = 0;
   if constexpr (sizeof(T) <= 32)
      clz = __builtin_clzl(n); // unsigned long
   else if (sizeof(T) <= 64)
      clz = __builtin_clzll(n); // unsigned long long
   else { // See https://stackoverflow.com/a/40528716
      uint64_t hi = n >> 64;
      uint64_t lo = (hi == 0) ? n : -1ULL;
      clz = _lzcnt_u64(hi) + _lzcnt_u64(lo);
   }
   return T{1} << (CHAR_BIT * sizeof(T) - clz);
}

другие компиляторы

если вы используете компилятор, отличный от GCC или Clang, пожалуйста, посетите страницу Википедии со списком Подсчет Ведущих Нулей функции побитового:

  • Visual C++ 2005 = > Заменить __builtin_clzl() by _BitScanForward()
  • Visual C++ 2008 = > Заменить __builtin_clzl() by __lzcnt()
  • icc = > заменить __builtin_clzl() by _bit_scan_forward
  • GHC (Haskell) => заменить __builtin_clzl() by countLeadingZeros()

вклад Добро пожаловать!

пожалуйста, предложите улучшения в комментариях. Также предложите альтернативу для используемого компилятора или вашего языка программирования...

Смотрите также похожие ответы

Как насчет чего-то вроде этого:

int pot = 1;
for (int i = 0; i < 31; i++, pot <<= 1)
    if (pot >= x)
        break;

вам просто нужно найти самый значительный бит и сдвинуть его влево один раз. Вот реализация Python. Я думаю, что x86 имеет инструкцию для получения MSB, но здесь я реализую все это в прямом Python. После того, как у вас есть MSB это легко.

>>> def msb(n):
...     result = -1
...     index = 0
...     while n:
...         bit = 1 << index
...         if bit & n:
...             result = index
...             n &= ~bit
...         index += 1
...     return result
...
>>> def next_pow(n):
...     return 1 << (msb(n) + 1)
...
>>> next_pow(1)
2
>>> next_pow(2)
4
>>> next_pow(3)
4
>>> next_pow(4)
8
>>> next_pow(123)
128
>>> next_pow(222)
256
>>>

забудь об этом! Он использует петлю !

     unsigned int nextPowerOf2 ( unsigned int u)
     {
         unsigned int v = 0x80000000; // supposed 32-bit unsigned int

         if (u < v) {
            while (v > u) v = v >> 1;
         }
         return (v << 1);  // return 0 if number is too big
     }
private static int nextHighestPower(int number){
    if((number & number-1)==0){
        return number;
    }
    else{
        int count=0;
        while(number!=0){
            number=number>>1;
            count++;
        }
        return 1<<count;
    }
}
// n is the number
int min = (n&-n);
int nextPowerOfTwo = n+min;
#define nextPowerOf2(x, n) (x + (n-1)) & ~(n-1)

или даже

#define nextPowerOf2(x, n)  x + (x & (n-1))