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


94

我需要这样的功能:

// return true iff '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还有其他东西,例如Math例程中的东西。
jww

Answers:


190

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

http://www.graphics.stanford.edu/~seander/bithacks.html包含大量的巧妙的比特稀疏算法,包括该算法。


8
基本上如此(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal或n && !(n & (n - 1))作为答案状态内的链接。
卡斯滕

为什么,哦为什么,这不是答案的顶部吗?OP,请接受。
donturner

@SaurabhGoyal一个小的改进是这样的:n & !(n & (n - 1))。注意按位AND &(不是逻辑和&&)。按位运算符不会实现短路,因此代码不会分支。在可能发生分支错误预测并且计算表达式的rhs(即!(n & (n - 1)))便宜的情况下,这是首选方法。
卡西欧·内里

@cassio !是逻辑运算符,因此value !(n & (n - 1))会是一个布尔值,您确定可以将按位AND运算符指定为布尔值和数字吗?如果是,那么看起来不错。
萨拉·戈亚尔

81

2的幂只能设置一位(用于无符号数字)。就像是

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

会很好的工作;较低有效位中的1小于2的幂是全1,因此必须按位与AND为0。

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


您错过了“!” 或“== 0”

您还缺少x负值的测试。
罗伯·威尔斯

整洁,您如何编辑它而没有出现“ x分钟前已编辑”?

认真地说,您是如何获得120 rep的答案的?

@Mike F:确实,似乎人们会在不检查答案的情况下对答案进行投票。我猜任何人都可能犯错-如果将来将来犯任何错误,请随时将其删除。
亚当·赖特

49

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

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

注意,总是精确地设置1位。唯一的例外是带符号整数。例如,一个值为-128的8位带符号整数如下所示:

10000000

因此,在检查了该数字是否大于零之后,我们可以使用一个聪明的小技巧来测试仅设置了一位。

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

有关更多信息,请参见此处


14

方法1:

用数字除以2来进行检查。

时间复杂度: O(log2n)。

方法2:

按位与,该数字及其前一个数字应等于零。

示例: Number = 8 Binary of 8:1 0 0 0 Binary of 7:0 1 1 1和两个数字的按位与是0 0 0 0 = 0。

时间复杂度: O(1)。

方法3:

将数字与前一个数字进行按位XOR运算应为两个数字的总和。

示例: Number = 8 Binary of 8:1 0 0 0 Binary of 7:0 1 1 1和两个数字的按位XOR为1 1 1 1 = 15。

时间复杂度: O(1)。

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



7

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

n&(-n)== n

注意:该条件适用于n = 0,但不是2的幂。
之所以可行,原因是:
-n是n的2s补码。与n相比,-n将n的最右置位的每一位的左边翻转。对于2的幂,只有一个置1位。


2
我的意思是条件适用于n = 0,尽管它不是2的幂
FReeze FRancis

如果n是无符号的,这可以解决转换吗?
约瑟夫·加文

5

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

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

5

如果使用GCC,这可能是最快的。它仅使用POPCNT cpu指令和一个比较。任何2的幂的二进制表示,始终只设置了一位,其他位始终为零。因此,我们用POPCNT来计算设置位的数量,如果等于1,则该数量为2的幂。我认为没有任何可能的更快方法。如果您一次了解它,它就非常简单:

if(1==__builtin_popcount(n))

不。我刚刚测试过。我喜欢popcount,但是对于2次幂测试i && !(i & (i - 1))),即使我确定要在gcc中启用本机程序集POPCNT指令,该测试在我的计算机上也要快10%左右。
埃拉乌尔

糟糕,我收回了。我的测试程序循环运行,分支预测“作弊”。没错,如果您的CPU上有POPCNT指令,它将更快。
埃拉乌尔

3

由于布尔短路和比较慢的事实,因此跟着投票最多的答案相比,跟随会更快。

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

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

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


3

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

如果您具有带位操作说明的现代英特尔处理器,则可以执行以下操作。它省略了直接的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可用并启用时,在Visual Studio 2015及更高版本中的Microsoft编译器中可用。有关所需的标头,请参见SIMD内部函数的标头文件

我平时守卫_blsr_u64_LP64_的情况下,编译在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中要求的“最简单的方法”,而是对于特定环境而言最快的方法。展示如何针对不同的体系结构进行条件化非常有用。
fearless_fool19年

1

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

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

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


它可能不快也可能不短,但与最重要的答案不同,它是正确的。

2
在发表评论时,他们都被打扰了。此后已将它们编辑为可接受的状态。

0

这是另一种方法,在这种情况下使用|代替&

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

0

通过c ++是可能的

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

2
对我而言,这既不简单,也不快速。
luk32

2
也就是说,由于log2,它肯定不是很快,并且证明它的工作原理不是那么容易解释(准确地说,您是否会被舍入错误所困扰?)。它也不必要地与混淆if..return..else..return。折叠到return x==(double)y;哪里有什么问题?它应该返回boolanyayws。如果一个人真的愿意坚持,甚至IMO三元运算符也会更加清晰int
luk32

0

我知道这是一个老的帖子,但是我认为在这里发布可能会很有趣。


Code-Golf SE(因此,所有功劳归功于撰写此文件的人):语言展示

(关于C的段落,长度36的分段)

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

-1

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


2
也许没有关于:-)。
paxdiablo

1
这将带来浮点精度的问题。ln(1 << 29)/ ln(2)变为29.000000000000004。
匿名

-3

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

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

它比四次对数快得多(第一个设置为获取小数结果,第二个设置为获取整数集并进行比较)


5
很高兴看到也可以在T-SQL中实现该问题的最佳答案,但这与此处提出的问题并不真正相关。另一种选择(如果您正在T-SQL中寻找解决方案,找到了已回答的问题,在T-SQL中实现了该问题,并认为发布此答案足够有趣),则可以参考T-SQL来发布问题,然后自己回答,参考这个回答的问题。希望这个建议有帮助。
西蒙(Simon)

这并不能真正回答这个问题
phuclv 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.