如何测试一个数字是不是是 2 的幂?
Posted
技术标签:
【中文标题】如何测试一个数字是不是是 2 的幂?【英文标题】: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);
谁能建议我怎么写这个?
【问题讨论】:
Find if a number is a power of two without math function or log function的可能重复 @rootTraveller - 可能不是重复的。 C++ 和 Java 是不同的语言,各自提供不同的功能。例如,在 C/C++ 中,我们现在可以将内在函数与启用 BMI 的处理器一起使用,它发出机器指令以在一次时钟内完成。我想 Java 还有其他东西,比如可能来自数学例程的东西。 【参考方案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 & (n - 1)) == 0
是最好的。但是,请注意,对于 n=0,它会错误地返回 true,因此如果可能,您需要明确地检查它。
http://www.graphics.stanford.edu/~seander/bithacks.html 拥有大量聪明的比特旋转算法,包括这个。
【讨论】:
所以基本上(n>0 && ((n & (n-1)) == 0))
@SaurabhGoyal 或 n && !(n & (n - 1))
作为答案状态中的链接。
为什么,哦,为什么,这不是答案的顶部吗? OP,请接受。
@cassio !
是一个逻辑运算符,因此 !(n & (n - 1))
的值将是一个布尔值,您确定一个布尔值和一个数字可以给按位与运算符吗?如果是,它看起来不错。
@CassioNeri 你的破解不起作用。例如,如果 n=2,并且将 true
转换为 1,您将得到等于 0 的 10 & 1
。如果您希望它工作,您还必须将 n
转换为 bool
,即 bool(n) & !(n & (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】:这是另一种方法,在这种情况下使用|
而不是&
:
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;
有什么问题?它应该返回bool
anyayws。如果真的想坚持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 > 0) && _blsr_u32(x))
条件不正确,应为(x > 0) && (_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 && !(i & (i - 1)))
在我的机器上快了大约 10%,即使我确定在 gcc 中启用本机汇编 POPCNT 指令。
糟糕,我收回了。我的测试程序循环运行,分支预测是“作弊”。你是对的,如果你的 CPU 上有 POPCNT 指令,它会更快。
请注意,对于非 x86 架构,计算人口计数可能比传统检查慢。例如,在 AArch64 上,它通常需要 4 条指令:fmov
、cnt
、addv
、fmov
,其中第一条 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 > 0) && 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));
【讨论】:
以上是关于如何测试一个数字是不是是 2 的幂?的主要内容,如果未能解决你的问题,请参考以下文章