【问题标题】:Given an integer, how do I find the next largest power of two using bit-twiddling?给定一个整数,如何使用位旋转找到下一个最大的二的幂?
【发布时间】:2010-11-22 07:00:09
【问题描述】:

如果我有一个整数n,我如何通过按位移位或逻辑找到下一个数字k > n 使得k = 2^i,以及N 的一些i 元素。

示例:如果我有n = 123,我如何找到k = 128,它是2 的幂,而不是124,它只能被2 整除。这应该很简单,但它让我难以理解。

【问题讨论】:

标签: language-agnostic bit-manipulation


【解决方案1】:

对于 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 位。将该值加 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 行)。

【讨论】:

  • @AndreasT:没问题! (顺便说一句,要了解为什么需要一直达到 n >> 16,请考虑如果 n 已经是 2 的幂会发生什么情况。您将只有一个 1 位需要覆盖所有前面的位,这就是为什么所有的转变都是必要的。)
  • +1 是的,好技巧,我喜欢 :) 可以在这里找到更多小技巧:graphics.stanford.edu/~seander/bithacks.html
  • 虽然这行得通,但谁能解释一下它为什么行得通?
  • @endolith 这需要 log N 班次。这需要 log(log N) 个班次,因此它使用的班次更少。
  • 它是k >= n,而不是k > n
【解决方案2】:

这个其实有汇编解决方案(从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 的幂,虽然这不会返回它
【解决方案3】:

一种更数学的方式,没有循环:

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

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

【讨论】:

  • 谢谢。但我正在寻找一种“有点扭曲”的方式。 ;-)
  • +1,不是 OP 想要的,但奇怪的是,我需要一个适合单个内联表达式的答案:2 ^ (floor(log(x) / log(2)) + 1)
  • 它为您提供现有值。下一部分,y+1 让你获得下一个最大的力量。
  • @DanDan: 但如果 n 已经是 2 的幂,它会给出 2 的下一个幂,而不是返回 n
  • @endolith:如果n 是2 的幂,并且您想获得n 而不是“二的下一个幂”,则可以使用2 ^ ( ceil(log(x) / log(2)) ) 代替。但这不是问题(……我怎样才能找到下一个号码k > n……)
【解决方案4】:

这是一个合乎逻辑的答案:

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

【讨论】:

  • 哈,这么简单!没想到那个:)
【解决方案5】:

这是 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 >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()

【讨论】:

  • 如果你打算使用位移,你最好用shift &lt;&lt;= 1而不是shift *= 2。不确定这在 Python 中是否真的更快,但应该是。
【解决方案6】:

大于/大于等于

以下 sn-ps 用于 下一个数 k > n 使得 k = 2^i
(n=123 => k=128, n=128 => k=256) 由 OP 指定。

如果您希望 2 的最小幂大于 OR 等于 n,那么只需在上述 sn-ps 中将 __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));
}

根据martinec 的建议使用CHAR_BIT 进行增强

#include <cstdint>

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

使用 GCC 或 Clang 的 C++17(从 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 的 Wikipedia 页面:

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

欢迎投稿

请在 cmets 内提出改进建议。还为您使用的编译器或您的编程语言提出替代方案...

另见类似答案

【讨论】:

    【解决方案7】:

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

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

    可以在here 找到这个以及许多其他小技巧,包括 John Feminella 提交的 on。

    【讨论】:

    • wtf! 8-) ,将检查出来。谢谢
    【解决方案8】:

    假设 x 不是负数。

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

    【讨论】:

      【解决方案9】:

      如果您使用 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()

      【讨论】:

      【解决方案10】:
      function Pow2Thing(int n)
      {
          x = 1;
          while (n>0)
          {
              n/=2;
              x*=2;
          }
          return x;
      }
      

      【讨论】:

        【解决方案11】:

        你说有点玩弄?

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

        【讨论】:

        • 您可以安全地将您的while 替换为do/while 并保存一个测试,因为您已经使用前面的if 语句确认了初始测试。
        • 谢谢@seh;使用do while 改进。
        • 这里最快的方法,为什么没有upvotes?虽然,我换成了while(t &gt; (t &amp; -t))
        • @primo 至少在 C# 中,对于 32 位整数,接受的答案平均快 2.5 倍以上。
        【解决方案12】:

        这样的事情怎么样:

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

        【讨论】:

          【解决方案13】:

          您只需找到最高有效位并将其左移一次。这是一个 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
          >>>
          

          【讨论】:

          • 在 Python 答案中谈论汇编没有任何价值,因为 Python 使用字节码...
          【解决方案14】:

          忘记这个!它使用循环!

               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
               }
          

          【讨论】:

            【解决方案15】:
            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;
                }
            }
            

            【讨论】:

              【解决方案16】:
              // n is the number
              int min = (n&-n);
              int nextPowerOfTwo = n+min;
              

              【讨论】:

              • 这个想法是:填写所有 1,直到最大,当前为“on”位?如果是这样,是n+min+1吗?
              • 这失败了。 (5 & -5) = 1; 5 + 1 = 6。
              【解决方案17】:
              #define nextPowerOf2(x, n) (x + (n-1)) & ~(n-1)
              

              甚至

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

              【讨论】:

                猜你喜欢
                • 2016-11-29
                • 1970-01-01
                • 1970-01-01
                • 2012-09-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-08-11
                相关资源
                最近更新 更多