【问题标题】:How can I test whether a number is a power of 2?如何测试一个数字是否是 2 的幂?
【发布时间】:2010-09-11 14:53:50
【问题描述】:

我需要这样的函数:

// return true if 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  
// is_power_of_2(3) => false
bool is_power_of_2(int n);

谁能建议我怎么写这个?

【问题讨论】:

  • @rootTraveller - 可能不是重复的。 C++ 和 Java 是不同的语言,各自提供不同的功能。例如,在 C/C++ 中,我们现在可以将内在函数与启用 BMI 的处理器一起使用,它发出机器指令以在一次时钟内完成。我想 Java 还有其他东西,比如可能来自数学例程的东西。

标签: c++ algorithm bit-manipulation


【解决方案1】:

2 的幂将只设置一个位(对于无符号数)。类似的东西

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

可以正常工作;小于 2 的 1 是低有效位中的所有 1,因此必须按位与 0。

当我假设无符号数字时,== 0 测试(我最初忘记了,抱歉)就足够了。如果您使用有符号整数,您可能需要 > 0 测试。

【讨论】:

  • 你少了一个“!”或'==0'
  • 您还缺少对 x 负值的测试。
  • 很好,你是如何编辑它而不出现“x 分钟前编辑”的?
  • 说真的,你是如何因为一个明显错误的答案而获得 120 次代表的?
  • @Mike F:确实,似乎人们会在不检查答案的情况下对答案进行投票。我猜任何人都可能犯错 - 如果我将来犯了任何错误,请随时修改。
【解决方案2】:
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}

【讨论】:

    【解决方案3】:

    二进制中二的幂如下所示:

    1: 0001
    2: 0010
    4: 0100
    8: 1000
    

    请注意,始终只有 1 位设置。唯一的例外是有符号整数。例如一个值为 -128 的 8 位有符号整数如下所示:

    10000000
    

    所以在检查了这个数字是否大于零之后,我们可以使用一个聪明的小技巧来测试一个并且只有一个位被设置。

    bool is_power_of_2(int x) {
        return x > 0 && !(x & (x−1));
    }
    

    更多小知识请看here

    【讨论】:

      【解决方案4】:

      这不是最快或最短的方法,但我认为它非常易读。所以我会做这样的事情:

      bool is_power_of_2(int n)
        int bitCounter=0;
        while(n) {
          if ((n & 1) == 1) {
            ++bitCounter;
          }
          n >>= 1;
        }
        return (bitCounter == 1);
      }
      

      这是有效的,因为二进制是基于 2 的幂。任何只设置一位的数字都必须是 2 的幂。

      【讨论】:

      • 它可能不是很快也不是很短,但它与热门答案不同。
      • 在评论时他们都被窃听了。此后,它们已被编辑为可接受的状态。
      【解决方案5】:

      (n &amp; (n - 1)) == 0 是最好的。但是,请注意,对于 n=0,它会错误地返回 true,因此如果可能,您需要明确地检查它。

      http://www.graphics.stanford.edu/~seander/bithacks.html 拥有大量聪明的比特旋转算法,包括这个。

      【讨论】:

      • 所以基本上(n&gt;0 &amp;&amp; ((n &amp; (n-1)) == 0))
      • @SaurabhGoyal 或 n &amp;&amp; !(n &amp; (n - 1)) 作为答案状态中的链接。
      • 为什么,哦,为什么,这不是答案的顶部吗? OP,请接受。
      • @cassio ! 是一个逻辑运算符,因此 !(n &amp; (n - 1)) 的值将是一个布尔值,您确定一个布尔值和一个数字可以给按位与运算符吗?如果是,它看起来不错。
      • @CassioNeri 你的破解不起作用。例如,如果 n=2,并且将 true 转换为 1,您将得到等于 0 的 10 &amp; 1。如果您希望它工作,您还必须将 n 转换为 bool,即 bool(n) &amp; !(n &amp; (n - 1)) .
      【解决方案6】:

      另一种方法(可能不是最快的)是确定 ln(x) / ln(2) 是否为整数。

      【讨论】:

      • 没有可能 :-)。
      • 这会有浮点不准确的问题。 ln(1
      【解决方案7】:

      这是 T-SQL (SQL Server) 中的位移方法:

      SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo
      

      比做四次对数要快很多(第一次得到十进制结果,第二次得到整数集并比较)

      【讨论】:

      • 很高兴看到这个问题的最佳答案也可以在 T-SQL 中实现,但这与这里提出的问题并不真正相关。另一种选择(如果您正在寻找 T-SQL 中的解决方案,找到这个已回答的问题,在 T-SQL 中实现它并认为发布此答案足够有趣)将参考 T-SQL 发布问题,然后参考这个已回答的问题,自己回答。希望这个建议对您有所帮助。
      • 这并不能真正回答这个问题
      【解决方案8】:

      这是另一种方法,在这种情况下使用| 而不是&amp;

      bool is_power_of_2(int x) {
          return x > 0 && (x<<1 == (x|(x-1)) +1));
      }
      

      【讨论】:

        【解决方案9】:

        可以通过c++实现

        int IsPowOf2(int z) {
        double x=log2(z);
        int y=x;
        if (x==(double)y)
        return 1;
        else
        return 0;
        }
        

        【讨论】:

        • 这对我来说既不简单也不快。
        • 即由于log2,它肯定不会很快,而且证明它的工作并不容易解释(准确地说,你会被舍入错误抓住吗?)。它也不必要地与if..return..else..return 混淆。将其折叠到 return x==(double)y; 有什么问题?它应该返回boolanyayws。如果真的想坚持int,IMO 甚至三元运算符都会更清楚。
        【解决方案10】:

        由于布尔短路和比较慢的事实,跟随会比大多数投票的答案更快。

        int isPowerOfTwo(unsigned int x)
        {
          return x && !(x & (x – 1));
        }
        

        如果你知道x不能为0,那么

        int isPowerOfTwo(unsigned int x)
        {
          return !(x & (x – 1));
        }
        

        【讨论】:

          【解决方案11】:

          方法一:

          将数字除以 2 以检查它。

          时间复杂度: O(log2n)。

          方法 #2:

          按位与前一个数字的数字应等于零。

          示例: 数字 = 8 8的二进制:1 0 0 0 7 的二进制:0 1 1 1 和两个数字的按位与是 0 0 0 0 = 0。

          时间复杂度: O(1)。

          方法 #3:

          按位 XOR 与前一个数字的数字应该是两个数字的总和。

          示例: 数字 = 8 8的二进制:1 0 0 0 7 的二进制数:0 1 1 1 和两个数字的按位异或是 1 1 1 1 = 15。

          时间复杂度: O(1)。

          http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html

          【讨论】:

            【解决方案12】:

            对于 2 的任何幂,以下也成立。

            n&(-n)==n

            注意:n=0 的条件为真,尽管它不是 2 的幂。
            这样做的原因是:
            -n 是 n 的 2s 补码。 -n 将与 n 相比,将 n 的最右边设置位左侧的每一位翻转。对于 2 的幂,只有一个设置位。

            【讨论】:

            • 我的意思是 n=0 的条件为真,尽管它不是 2 的幂
            • 这是否适用于 n 无符号时发生的转换?
            【解决方案13】:

            在 C++ 中测试一个数字是否是 2 的幂的最简单方法是什么?

            如果您有一个带有Bit Manipulation Instructions 的现代英特尔处理器,那么您可以执行以下操作。它省略了直接的 C/C++ 代码,因为其他人已经回答了它,但如果 BMI 不可用或未启用,则需要它。

            bool IsPowerOf2_32(uint32_t x)
            {
            #if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
                return !!((x > 0) && _blsr_u32(x));
            #endif
                // Fallback to C/C++ code
            }
            
            bool IsPowerOf2_64(uint64_t x)
            {
            #if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
                return !!((x > 0) && _blsr_u64(x));
            #endif
                // Fallback to C/C++ code
            }
            

            GCC、ICC 和 Clang 信号 BMI 支持 __BMI__。当AVX2 is available and enabled 时,它在 Visual Studio 2015 及更高版本的 Microsoft 编译器中可用。对于您需要的标头,请参阅Header files for SIMD intrinsics

            我通常用_LP64_ 保护_blsr_u64,以防在i686 上编译。 Clang 需要一些解决方法,因为它使用了稍微不同的内在符号 nam:

            #if defined(__GNUC__) && defined(__BMI__)
            # if defined(__clang__)
            #  ifndef _tzcnt_u32
            #   define _tzcnt_u32(x) __tzcnt_u32(x)
            #  endif
            #  ifndef _blsr_u32
            #    define  _blsr_u32(x)  __blsr_u32(x)
            #  endif
            #  ifdef __x86_64__
            #   ifndef _tzcnt_u64
            #    define _tzcnt_u64(x) __tzcnt_u64(x)
            #   endif
            #   ifndef _blsr_u64
            #     define  _blsr_u64(x)  __blsr_u64(x)
            #   endif
            #  endif  // x86_64
            # endif  // Clang
            #endif  // GNUC and BMI
            

            你能告诉我一个可以找到这种算法的好网站吗?

            这个网站经常被引用:Bit Twiddling Hacks

            【讨论】:

            • 这当然不是 OP 中要求的“最简单的方法”,但可以说是特定环境中最快的。展示如何针对不同架构进行条件化非常有用。
            • !!((x &gt; 0) &amp;&amp; _blsr_u32(x)) 条件不正确,应为(x &gt; 0) &amp;&amp; (_blsr_u32(x) == 0)
            【解决方案14】:
            return n > 0 && 0 == (1 << 30) % n;
            

            【讨论】:

              【解决方案15】:

              如果使用 GCC,这可能是最快的。它只使用一个 POPCNT cpu 指令和一个比较。任何 2 数的幂的二进制表示,始终只有一位设置,其他位始终为零。所以我们用 POPCNT 计算设置的位数,如果它等于 1,则该数字是 2 的幂。我认为没有任何可能的更快的方法。而且很简单,如果你理解一次:

              if(1==__builtin_popcount(n))
              

              【讨论】:

              • 不。我刚刚测试了这个。我喜欢 popcount,但对于 2 的幂测试,测试 i &amp;&amp; !(i &amp; (i - 1))) 在我的机器上快了大约 10%,即使我确定在 gcc 中启用本机汇编 POPCNT 指令。
              • 糟糕,我收回了。我的测试程序循环运行,分支预测是“作弊”。你是对的,如果你的 CPU 上有 POPCNT 指令,它会更快。
              • 请注意,对于非 x86 架构,计算人口计数可能比传统检查慢。例如,在 AArch64 上,它通常需要 4 条指令:fmovcntaddvfmov,其中第一条 fmov 指令将值从通用寄存器复制到 SIMD 寄存器,最后一条fmov 指令将计算的人口计数复制回通用寄存器。
              【解决方案16】:

              在 C++20 中有 std::has_single_bit,如果您不需要自己实现它,您可以将其用于此目的:

              #include <bit>
              static_assert(std::has_single_bit(16));
              static_assert(!std::has_single_bit(15));
              

              请注意,这要求参数是无符号整数类型。

              【讨论】:

              • 请注意,它已重命名为std::has_single_bit,并且仅针对无符号整数类型定义。对于有符号整数类型,您可能还需要检查该值是否为正,以避免错误地将最小有符号整数值(如 INT_MIN)视为 2 的幂:(x &gt; 0) &amp;&amp; std::has_single_bit((unsigned)x)
              【解决方案17】:

              我知道这是一篇非常旧的帖子,但我认为在这里发帖可能会很有趣。


              来自 Code-Golf SE(感谢写这篇文章的人):Showcase of Languages

              (关于C的段落,长度36 sn-p小段)

              bool is_pow_of_2(const unsigned long long &num){return!!num&!(num&(num-1));}
              

              【讨论】:

                猜你喜欢
                • 2011-02-02
                • 2010-10-10
                • 1970-01-01
                • 2015-10-12
                • 1970-01-01
                • 2017-01-09
                • 1970-01-01
                相关资源
                最近更新 更多