【问题标题】:Rounding up to next power of 2四舍五入到 2 的下一个幂
【发布时间】:2010-10-02 17:54:08
【问题描述】:

我想写一个函数来返回最接近的 2 的下一个幂。例如,如果我的输入是 789,则输出应该是 1024。有没有什么方法可以在不使用任何循环而只使用一些位运算符的情况下实现这一点?

【问题讨论】:

标签: c optimization bit-manipulation


【解决方案1】:

检查Bit 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++;

其他宽度的扩展应该很明显。

【讨论】:

  • 这不是最有效的解决方案,因为许多处理器都有用于计算前导零的特殊指令,可用于非常有效地计算 log2。见en.wikipedia.org/wiki/Find_first_set
  • @Simon:这是便携式解决方案。没有适用于所有架构的通用高效算法
  • 如果数字本身是 2 的幂怎么办?
  • 这个线程仍然被很好地引用但是这个答案(和大多数其他人)已经非常过时了。 CPU 有一条指令可以帮助解决这个问题(实际上当时已经?)。来自:jameshfisher.com/2018/03/30/round-up-power-2.htmluint64_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(我认为是 Clang?),但最好花时间找到对 CLZ 的调用而不是复制粘贴周围的所有选项。
  • @MappaM 这个答案仍然是高度相关的,也是最好的便携方式。如果x > UINT32_MAX 并且不是无分支的,则您的 64 位版本具有未定义的行为。此外,GCC 和 Clang 默认使用 -mtune=generic(与大多数发行版一样),因此您的代码不会扩展到 x86_64 上的 lzcnt 指令——它实际上会扩展为慢得多的东西(libgcc 例程),除非您使用类似-march=native 的东西。因此,您提议的替换是不可移植的、有缺陷的并且(通常)速度较慢。
【解决方案2】:
next = pow(2, ceil(log(x)/log(2)));

这可以通过找到你要提高 2 以获得 x 的数字来工作(取数字的对数,然后除以所需基数的对数,see wikipedia for more)。然后用 ceil 将其四舍五入以获得最接近的整数幂。

这是一种比其他地方链接的按位方法更通用(即更慢!)的方法,但是很高兴了解数学,是吗?

【讨论】:

  • 从 C99 开始,如果您的工具支持,您也可以只使用 log2。 GCC 和 VS 似乎没有:(
  • 不过,请注意浮点精度。 log(pow(2,29))/log(2) = 29.000000000000004,所以结果是 230 而不是返回 229。我认为这就是 log2 函数存在的原因?
  • 这样做的成本可能至少为 200 个周期,甚至不正确。为什么会有这么多赞?
  • @SuperflyJon 但它提到了按位运算符,除非另有说明,否则我认为任何问题都暗示了正确性。
  • 请更新答案并使用 log2()。我犯了一个错误,没有阅读cmets,最终在CP比赛中输了。 :(
【解决方案3】:

我认为这也有效:

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

答案是power

【讨论】:

  • 公平的问题要求没有循环。但与其他一些函数一样聪明,对于对性能不敏感的代码,快速、容易理解并验证为正确的答案总是对我有利。
  • 这不是返回最接近的 2 的幂,但它的幂立即大于 X。仍然非常好
  • 可以使用一些按位“魔术”来代替乘法power &lt;&lt;= 1
  • @Vallentin 这应该由编译器自动优化。
  • 如果x 太大(即没有足够的位来表示 2 的下一个幂),请注意无限循环。
【解决方案4】:
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;

}

【讨论】:

  • 如果你把它归因于它会很好(除非你发现它)。它来自 bit twiddling hacks 页面。
  • 是 32 位数字吗? 64 位扩展?
  • 乔纳森,你需要为上半部分做,如果为零,你为下半部分做。
  • @florin,如果 v 是 64 位类型,你不能在 16 之后添加一个“c |= v >> 32”吗?
  • 只适用于特定位宽的代码应该使用固定宽度类型而不是最小宽度类型。这个函数应该接受并返回一个uint32_t
【解决方案5】:

如果您使用的是 GCC,您可能想看看 Lockless Inc. 的 Optimizing the next_pow2() function。此页面描述了一种使用内置函数 builtin_clz()(计数前导零)的方法,然后直接使用 x86 (ia32) 汇编指令bsr(位扫描反向),就像在another answerlink to gamedev site 中描述的一样。这段代码可能比previous answer 中描述的更快。

顺便说一句,如果你不打算使用汇编指令和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));
}

【讨论】:

  • 请注意,这将返回大于或等于 x 的 2 的最小幂。将 (x -1) 更改为 x 会更改函数以返回大于 x 的 2 的较小幂。
  • 你可以在 Visual C++ 上使用_BitScanForward
  • 你也可以使用__builtin_ctz()
  • @MarkP __builtin_ctz() 将无法将任何非 2 数的幂四舍五入到下一个 2 的幂
  • 请在您的答案中添加指向其他编译器的内置按位函数的维基百科列表的链接:en.wikipedia.org/wiki/Find_first_set#Tool_and_library_support                                请同时提供 64 位版本。我提出以下 C++11 函数:             constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL&lt;&lt;(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
【解决方案6】:

还有一个,虽然我用的是循环,但是这比数学操作数快很多

两个“楼层”选项的幂:

int power = 1;
while (x >>= 1) power <<= 1;

两个“ceil”选项的幂:

int power = 2;
x--;    // <<-- UPDATED
while (x >>= 1) power <<= 1;

更新

如 cmets 中所述,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) &gt; 0 &amp;&amp; !((x) &amp; (x-1)))
  • @zorksylar 更有效的是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
【解决方案7】:

对于任何无符号类型,以 Bit 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;
}

那里并没有真正的循环,因为编译器在编译时知道迭代次数。

【讨论】:

  • 请注意,问题是关于 C 的。
  • @martinkunev 只需替换 UnsignedType 并手动处理即可。我很确定 C 程序员可以扩展这个简单的模板而忽略std::is_unsigned&lt;UnsignedType&gt;::value 断言。
  • @user877329 当然,如果有人想把它翻译成 C,用 Javascript 也能得到答案会很好。
  • @martinkunev UnsignedType 在 JavaScript 中?无论如何,这个解决方案展示了如何为任何 UnsignedType 执行此操作,并且它恰好是用 C++ 编写的,而不是伪代码 [ sizeof(v)*CHAR_BIT 而不是 UnsignedType 对象中的位数]。
【解决方案8】:

尽管这个问题被标记为c 这里我的五美分。幸运的是,C++ 20 将包括 std::ceil2std::floor2(参见 here)。它是consexpr 模板函数,当前GCC implementation 使用位移位并​​适用于任何整数无符号类型。

【讨论】:

【解决方案9】:

对于 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 将为您提供 x86 上整数的 log2。它计算设置了多少正确位,恰好等于该数字的 log2。其他处理器(通常)具有类似的指令,例如 CLZ,并且根据您的编译器,可能有一个内部函数可以为您完成工作。

【讨论】:

  • 这是一个有趣的问题,尽管与问题无关(我只想四舍五入整数),我会试试这个......
  • 在阅读了关于浮动的维基百科文章后想出了它。除此之外,我还用它来计算整数精度的平方根。也不错,但更不相关。
  • 这打破了严格的别名规则。在某些编译器上,它可能无法工作或发出警告。
【解决方案10】:

在标准c++20 中,这包含在&lt;bit&gt; 中。 答案很简单

#include <bit>
unsigned long upper_power_of_two(unsigned long v)
{
    return std::bit_ceil(v);
}

注意: 我给出的解决方案是针对c++,而不是c,我会给出答案this 的问题,但它作为这个问题的副本被关闭了!

【讨论】:

    【解决方案11】:

    在 x86 中,您可以使用 sse4 位操作指令来加快速度。

    //assume input is in eax
    mov    ecx,31      
    popcnt edx,eax   //cycle 1
    lzcnt  eax,eax   //cycle 2
    sub    ecx,eax
    mov    eax,1
    cmp    edx,1     //cycle 3
    jle @done        //cycle 4 - popcnt says its a power of 2, return input unchanged
    shl    eax,cl    //cycle 5
    @done: rep ret   //cycle 5
    

    在 c 中你可以使用匹配的内在函数。

    或无跳转,它通过避免由于跳转而导致的错误预测来加速事情,但通过延长依赖链来减慢事情的速度。给代码计时,看看哪个最适合你。

    //assume input is in eax
    mov    ecx,31
    popcnt edx,eax    //cycle 1
    lzcnt  eax,eax
    sub    ecx,eax
    mov    eax,1      //cycle 2
    cmp    edx,1
    mov    edx,0     //cycle 3 
    cmovle ecx,edx   //cycle 4 - ensure eax does not change
    shl    eax,cl    
    @done: rep ret   //cycle 5
    

    【讨论】:

    • 也许我遗漏了一些东西,但看起来这不起作用。它基本上将值 2 左移原始数字中前导零的计数,即2&lt;&lt;lzcnt(x)。所以如果参数是 3,它有 30 个前导零,那么结果是 2&lt;&lt;30 = 2147483648,但结果应该是 4。
    • 你应该尽量避免分支:用mov edx,0; cmovle ecx,edx替换它
    【解决方案12】:
    /*
    ** 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 之间。该宏对于在编译时设置常量也很有用。

    【讨论】:

    • 这可能是最糟糕的解决方案(它还缺少 64 位常量上的 ULL 后缀)。这将在所有情况下为每个输入生成 32 个测试。最好使用 while 循环,它总是会更快或保持相同的速度。
    • 但是...如果输入是常数,预处理器可以评估这点,因此在运行时进行零操作!
    【解决方案13】:

    这是我在 C 中的解决方案。希望这会有所帮助!

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

    【讨论】:

      【解决方案14】:

      为了完整起见,这里是沼泽标准 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);
      }
      

      【讨论】:

      • 随机浏览器,如果你看了这个评论,就选择这个代码。这显然是最好的答案,没有特别的说明,没有一点玩弄,只是高效、可移植和标准的代码。猜猜为什么没有人支持它^^
      • 随机浏览器,如果您没有专门的浮点硬件,这将非常缓慢。在 x86 上,您可以使用 bit twiddling 围绕此代码转圈。 rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl 大约快 25 倍。
      【解决方案15】:

      在 C/C++ 中用于整数输入的高效 Microsoft(例如 Visual Studio 2017)特定解决方案。通过在检查最高有效 1 位的位置之前递减来处理输入与 2 的幂值完全匹配的情况。

      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 个左右的内联指令,类似于以下内容:

      dec eax
      bsr rcx, rax
      inc ecx
      mov eax, 1
      shl rax, cl
      

      显然,Visual Studio C++ 编译器并未针对编译时值进行优化,但那里并没​​有太多指令。

      编辑:

      如果您希望输入值 1 产生 1(2 的 0 次方),对上述代码稍作修改仍会生成没有分支的直通指令。

      inline unsigned int ExpandToPowerOf2(unsigned int Value)
      {
          unsigned long Index;
          _BitScanReverse(&Index, --Value);
          if (Value == 0)
              Index = (unsigned long) -1;
          return (1U << (Index + 1));
      }
      

      只生成更多指令。诀窍是 Index 可以被替换为一个测试,然后是一个 cmove 指令。

      【讨论】:

      • 一个小错误:它应该为 1 返回 1,但它没有。
      • 谢谢。在为其开发的应用程序中,当输入 1 时,我们明确需要 2 的第一次幂。 1 可以被视为带有条件的特殊情况,而不会产生我想象的太多指令。
      • 更新了答案以包含一个对于输入值 1 返回 1 的版本。
      【解决方案16】:

      您可能会发现以下说明对您的目的有所帮助:

      【讨论】:

        【解决方案17】:

        试图为此制定“终极”解决方案。以下代码

        • 针对 C 语言(不是 C++),

        • 如果编译器支持,则使用编译器内置函数生成高效代码 (CLZ or BSR instruction),

        • 是可移植的(标准 C 和无汇编),内置函数除外,并且

        • 解决所有未定义的行为。

        如果您使用 C++ 编写,您可以适当地调整代码。请注意,C++20 引入了 std::bit_ceil,它的作用完全相同,只是在某些条件下行为可能未定义。

        #include <limits.h>
        
        #ifdef _MSC_VER
        # if _MSC_VER >= 1400
        /* _BitScanReverse is introduced in Visual C++ 2005 and requires
           <intrin.h> (also introduced in Visual C++ 2005). */
        #include <intrin.h>
        #pragma intrinsic(_BitScanReverse)
        #pragma intrinsic(_BitScanReverse64)
        #  define HAVE_BITSCANREVERSE 1
        # endif
        #endif
        
        /* Macro indicating that the compiler supports __builtin_clz().
           The name HAVE_BUILTIN_CLZ seems to be the most common, but in some
           projects HAVE__BUILTIN_CLZ is used instead. */
        #ifdef __has_builtin
        # if __has_builtin(__builtin_clz)
        #  define HAVE_BUILTIN_CLZ 1
        # endif
        #elif defined(__GNUC__)
        # if (__GNUC__ > 3)
        #  define HAVE_BUILTIN_CLZ 1
        # elif defined(__GNUC_MINOR__)
        #  if (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
        #   define HAVE_BUILTIN_CLZ 1
        #  endif
        # endif
        #endif
        
        /**
         * Returns the smallest power of two that is not smaller than x.
         */
        unsigned long int next_power_of_2_long(unsigned long int x)
        {
            if (x <= 1) {
                return 1;
            }
            x--;
        
        #ifdef HAVE_BITSCANREVERSE
            if (x > (ULONG_MAX >> 1)) {
                return 0;
            } else {
                unsigned long int index;
                (void) _BitScanReverse(&index, x);
                return (1UL << (index + 1));
            }
        #elif defined(HAVE_BUILTIN_CLZ)
            if (x > (ULONG_MAX >> 1)) {
                return 0;
            }
            return (1UL << (sizeof(x) * CHAR_BIT - __builtin_clzl(x)));
        #else
            /* Solution from "Bit Twiddling Hacks"
               <http://www.graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2>
               but converted to a loop for smaller code size.
               ("gcc -O3" will unroll this.) */
            {
                unsigned int shift;
                for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
                    x |= (x >> shift);
                }
            }
            return (x + 1);
        #endif
        }
        
        unsigned int next_power_of_2(unsigned int x)
        {
            if (x <= 1) {
                return 1;
            }
            x--;
        
        #ifdef HAVE_BITSCANREVERSE
            if (x > (UINT_MAX >> 1)) {
                return 0;
            } else {
                unsigned long int index;
                (void) _BitScanReverse(&index, x);
                return (1U << (index + 1));
            }
        #elif defined(HAVE_BUILTIN_CLZ)
            if (x > (UINT_MAX >> 1)) {
                return 0;
            }
            return (1U << (sizeof(x) * CHAR_BIT - __builtin_clz(x)));
        #else
            {
                unsigned int shift;
                for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
                    x |= (x >> shift);
                }
            }
            return (x + 1);
        #endif
        }
        
        unsigned long long next_power_of_2_long_long(unsigned long long x)
        {
            if (x <= 1) {
                return 1;
            }
            x--;
        
        #if (defined(HAVE_BITSCANREVERSE) && \
            ULLONG_MAX == 18446744073709551615ULL)
            if (x > (ULLONG_MAX >> 1)) {
                return 0;
            } else {
                /* assert(sizeof(__int64) == sizeof(long long)); */
                unsigned long int index;
                (void) _BitScanReverse64(&index, x);
                return (1ULL << (index + 1));
            }
        #elif defined(HAVE_BUILTIN_CLZ)
            if (x > (ULLONG_MAX >> 1)) {
                return 0;
            }
            return (1ULL << (sizeof(x) * CHAR_BIT - __builtin_clzll(x)));
        #else
            {
                unsigned int shift;
                for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
                    x |= (x >> shift);
                }
            }
            return (x + 1);
        #endif
        }
        

        【讨论】:

          【解决方案18】:

          C# 中的可移植解决方案:

          int GetNextPowerOfTwo(int input) {
              return 1 << (int)Math.Ceiling(Math.Log2(input));
          }
          

          Math.Ceiling(Math.Log2(value)) 计算二的下一个幂的指数,1 &lt;&lt; 通过位移计算实际值。

          如果您拥有 .NET Core 3 或更高版本,则更快的解决方案:

          uint GetNextPowerOfTwoFaster(uint input) {
              return (uint)1 << (sizeof(uint) * 8 - System.Numerics.BitOperations.LeadingZeroCount(input - 1));
          }
          

          这使用System.Numerics.BitOperations.LeadingZeroCount(),它使用硬件指令(如果可用):

          https://github.com/dotnet/corert/blob/master/src/System.Private.CoreLib/shared/System/Numerics/BitOperations.cs

          更新:

          RoundUpToPowerOf2()Coming in .NET 6!内部实现与上面的 .NET Core 3 方案大体相同。

          这是community update

          【讨论】:

            【解决方案19】:

            用于 C++14 的 clp2 的 constexpr 版本

            #include <iostream>
            #include <type_traits>
            
            // Closest least power of 2 minus 1. Returns 0 if n = 0.
            template <typename UInt, std::enable_if_t<std::is_unsigned<UInt>::value,int> = 0>
              constexpr UInt clp2m1(UInt n, unsigned i = 1) noexcept
                { return i < sizeof(UInt) * 8 ? clp2m1(UInt(n | (n >> i)),i << 1) : n; }
            
            /// Closest least power of 2 minus 1. Returns 0 if n <= 0.
            template <typename Int, std::enable_if_t<std::is_integral<Int>::value && std::is_signed<Int>::value,int> = 0>
              constexpr auto clp2m1(Int n) noexcept
                { return clp2m1(std::make_unsigned_t<Int>(n <= 0 ? 0 : n)); }
            
            /// Closest least power of 2. Returns 2^N: 2^(N-1) < n <= 2^N. Returns 0 if n <= 0.
            template <typename Int, std::enable_if_t<std::is_integral<Int>::value,int> = 0>
              constexpr auto clp2(Int n) noexcept
                { return clp2m1(std::make_unsigned_t<Int>(n-1)) + 1; }
            
            /// Next power of 2. Returns 2^N: 2^(N-1) <= n < 2^N. Returns 1 if n = 0. Returns 0 if n < 0.
            template <typename Int, std::enable_if_t<std::is_integral<Int>::value,int> = 0>
              constexpr auto np2(Int n) noexcept
                { return clp2m1(std::make_unsigned_t<Int>(n)) + 1; }
            
            template <typename T>
              void test(T v) { std::cout << clp2(v) << std::endl; }
            
            int main()
            {
                test(-5);                          // 0
                test(0);                           // 0
                test(8);                           // 8
                test(31);                          // 32
                test(33);                          // 64
                test(789);                         // 1024
                test(char(260));                   // 4
                test(unsigned(-1) - 1);            // 0
                test<long long>(unsigned(-1) - 1); // 4294967296
            
                return 0;
            }
            

            【讨论】:

              【解决方案20】:

              许多处理器架构支持log base 2 或非常相似的操作——count leading zeros。许多编译器都有它的内在函数。见https://en.wikipedia.org/wiki/Find_first_set

              【讨论】:

              • 这与查找最高设置位 (=bsr) 或计算前导零无关。他想四舍五入到最接近的 2 次幂。答案是“减去 1,然后执行 bsr 并左移 1”。
              【解决方案21】:

              假设你有一个好的编译器并且它可以在我上面做一些之前的事情,但无论如何这工作!!!

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

              【讨论】:

                【解决方案22】:

                我正在尝试获得最接近 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;
                }
                

                【讨论】:

                  【解决方案23】:

                  改编了 Paul Dixon 对 Excel 的回答,效果很好。

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

                  【讨论】:

                    【解决方案24】:

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

                    【讨论】:

                      【解决方案25】:

                      如果输入是常量表达式,这就是我用来让它成为常量表达式的方法。

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

                      会很好地减少到一个常数。

                      【讨论】:

                        【解决方案26】:

                        g++ 编译器提供了一个计算前导零的内置函数 __builtin_clz:

                        所以我们可以这样做:

                        int nextPowerOfTwo(unsigned int x) {
                          return 1 << sizeof(x)*8 - __builtin_clz(x);
                        }
                        
                        int main () {
                          std::cout << nextPowerOfTwo(7)  << std::endl;
                          std::cout << nextPowerOfTwo(31) << std::endl;
                          std::cout << nextPowerOfTwo(33) << std::endl;
                          std::cout << nextPowerOfTwo(8)  << std::endl;
                          std::cout << nextPowerOfTwo(91) << std::endl;
                          
                          return 0;
                        }
                        

                        结果:

                        8
                        32
                        64
                        16
                        128
                        

                        但请注意,对于x == 0__builtin_clz 返回未定义。

                        【讨论】:

                        • 您的代码中还有另一个未定义的行为。当x &gt; (UINT_MAX / 2) 时,__builtin_clz 将返回 0 导致左移 (&lt;&lt;) 运算符溢出。另外,你应该使用1U,而不是1,作为&lt;&lt;的左操作数,否则你定义的域会更小((INT_MAX / 2)而不是(UINT_MAX / 2))。
                        【解决方案27】:

                        如果你需要 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;
                            }
                        }
                        

                        【讨论】:

                        • 弗洛林:是的。它在这里被用作循环,不是吗?
                        • DrJokepu - 我认为 florin 的意思是说 OP 要求提供无循环解决方案
                        【解决方案28】:

                        将其转换为浮点数,然后使用 .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

                        【讨论】:

                        • 注意这个答案是在python中
                        【解决方案29】:
                        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
                        
                        

                        【讨论】:

                          【解决方案30】:

                          如果你想要一个单行模板。在这里

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

                          【讨论】:

                          • 这是 C 或 C++ 中未定义的行为,会导致错误。在没有序列点的情况下多次修改n是无效的。你写的好像n-=1应该首先发生,但这里唯一的保证是n;之后包含它的新值,括号不会改变它。
                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-05-22
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-07-22
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多