给定一个整数,如何使用bit-twiddling找到下一个最大的2的幂?

时间:2009-08-24 13:43:02

标签: language-agnostic bit-manipulation

如果我有一个整数n,我怎样才能找到k > n的下一个数字k = 2^ii元素N按位移位或逻辑。

示例:如果我有n = 123,我怎样才能找到k = 128,它是2的幂,而不是124,它只能被2整除。这应该很简单,但我不知道。

17 个答案:

答案 0 :(得分:94)

对于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是从131开始的下一个最高2的力量。

如果用于表示整数的位数本身是2的幂,则可以继续有效且无限地扩展此技术(例如,为64位整数添加n >> 32行。) / p>

答案 1 :(得分:29)

实际上有一个汇编解决方案(自80386指令集以来)。

您可以使用BSR(位扫描反转)指令扫描整数中的最高位。

  

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

在较新的CPU中,您可以使用更快的lzcnt指令(又名rep bsr)。 lzcnt在一个周期内完成工作。

答案 2 :(得分:21)

更加数学化的方式,没有循环:

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

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

答案 3 :(得分:12)

这是一个逻辑答案:

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

答案 4 :(得分:8)

这里的John Feminella的答案是作为循环实现的,因此它可以处理Python's long integers

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&gt; 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

答案 5 :(得分:3)

大于/大于或等于

以下摘录适用于下一个数字k&gt; n使得k = 2 ^ i
(由OP指定,n = 123 =&gt; k = 128,n = 128 =&gt; k = 256)。

如果您希望 2的最小幂大于或等于n ,则只需在上述代码段中将__builtin_clzll(n)替换为__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以外的编译器,请访问列出Count Leading Zeroes bitwise functions的维基百科页面:

  • Visual C ++ 2005 =&gt;将__builtin_clzl()替换为_BitScanForward()
  • Visual C ++ 2008 =&gt;将__builtin_clzl()替换为__lzcnt()
  • icc =&gt;将__builtin_clzl()替换为_bit_scan_forward
  • GHC(Haskell)=&gt;将__builtin_clzl()替换为countLeadingZeros()

欢迎捐款

请在评论中提出改进建议。同时为您使用的编译器或编程语言提出替代方案......

参见类似答案

答案 6 :(得分:3)

这是一个没有循环但是使用中间浮动的狂野的。

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

这个以及许多其他令人讨厌的黑客,包括John Feminella提交的,都可以找到here

答案 7 :(得分: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()

答案 8 :(得分:2)

假设x不是负数。

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

答案 9 :(得分:1)

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

答案 10 :(得分:1)

比如说,你说呢?

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位。注:这仅适用于有符号数字以二进制补码编码的情况。

答案 11 :(得分:0)

忘了这个!它使用循环!

     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
     }

答案 12 :(得分:0)

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

答案 13 :(得分:0)

你只需要找到最重要的位并将其向左移一次。这是一个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
>>>

答案 14 :(得分:0)

这样的事情:

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

答案 15 :(得分:-3)

// n is the number
int min = (n&-n);
int nextPowerOfTwo = n+min;

答案 16 :(得分:-3)

  where
    "consume dia ts = {n . n ∈ (Markings dia) ∧ (∃ t ∈ ts . (Dest n) = t)}"

甚至

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