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

誰も私がこれを書く方法を提案できますか?この種のアルゴリズムが見つかる優れたWebサイトを教えてください。



@rootTraveller-おそらく重複していません。C ++とJavaは異なる言語であり、それぞれが異なる機能を提供します。たとえば、C / C ++では、BMI対応のプロセッサで組み込み関数を使用できるようになりました。これにより、機械語命令を発行して1クロックで実行できるようになります。Javaには、数学ルーチンからのものなど、他にもあると思います。
JWW

回答:


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))回答状態内のリンクとして。
Carsten

なぜ、ああ、なぜ、これが回答の一番上にないのですか?OPは受け入れてください。
ドンターナー2018年

@SaurabhGoyal 1つの小さな改善はこれですn & !(n & (n - 1))。ビット単位のAND &(論理andではない&&)に注意してください。ビットごとの演算子は短絡を実装しないため、コードは分岐しません。これは、分岐の予測ミスが発生する可能性が高く、式のrhs(つまり!(n & (n - 1)))を計算するのが安価な場合に適しています。
Cassio Neri

@cassio !は論理演算子であり、したがっての値は!(n & (n - 1))ブール値になります。ビット単位のAND演算子にブール値と数値を指定できますか?はいの場合、よさそうです。
Saurabh Goyal

81

2の累乗では、ビットが1つだけ設定されます(符号なしの数値の場合)。何かのようなもの

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

正常に動作します。2の累乗よりも1小さい値は、下位ビットがすべて1であるため、ビットごとにANDする必要があります。

符号なしの数値を想定していたので、== 0のテスト(最初は忘れてしまい、申し訳ありません)で十分です。符号付き整数を使用している場合は、0より大きいテストが必要な場合があります。


「!」がありません または '== 0'

また、xの負の値のテストもありません。
ロブ・ウェルズ

きちんと、「編集されたx分前」が表示されずにそれをどのように編集しましたか?

真剣に、どうしてあなたは明らかに間違った答えのために120人の担当者を得たのですか?

@マイクF:確かに、人々はそれらをチェックせずに答えに投票するようです。誰もが間違いを犯す可能性があると思います。もし将来間違いを犯したとしても、遠慮なく編集してください。
アダムライト

49

バイナリの2のべき乗は次のようになります。

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

常に正確に1ビットが設定されていることに注意してください。唯一の例外は、符号付き整数です。たとえば、値が-128の8ビット符号付き整数は次のようになります。

10000000

そのため、数値がゼロより大きいことを確認した後、巧妙なハックを使用して、1ビットのみが設定されていることをテストできます。

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

よりいじくり回すためにここを見てください


14

アプローチ#1:

数値を2で除算して確認します。

時間の複雑さ: O(log2n)。

アプローチ#2:

ビットごとのANDと直前の数値の数値はゼロに等しくなければなりません。

例: Number = 8 Binary of 8:1 0 0 0 Binary of 7:0 1 1 1そして、両方の数値のビットごとのANDは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

注:2のべき乗ではありませんが、n = 0の場合、条件はtrueです。
これが機能する理由は次のとおりです。
-nはnの2の補数です。-nは、nと比較して、nの右端の設定ビットの左側にあるすべてのビットを反転します。2の累乗の場合、設定ビットは1つだけです。


2
2のべき乗ではありませんが、n = 0の場合に条件が当てはまることを意味します
FReeze FRancis

これは、nが符号なしの場合に発生する変換で機能しますか?
ジョセフガービン

5

C ++ 20ではstd::ispow2、自分で実装する必要がない場合に、まさにこの目的で使用できるものがあります。

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

5

GCCを使用している場合、これがおそらく最速です。POPCNT cpu命令と1つの比較のみを使用します。2の累乗の2進数表現は、常に1つのビットセットのみを持ち、他のビットは常に0です。したがって、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の累乗であるかどうかをテストする最も簡単な方法は何ですか?

ビット操作命令を備えた最新のIntelプロセッサを使用している場合は、以下を実行できます。他の人がすでにそれに答えているので、ストレート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

この種のアルゴリズムが見つかる優れたWebサイトを教えてください。

このWebサイトはよく引用されます:ビットツイドルリングハック


これは、OPで要求されている「最も簡単な方法」ではありませんが、特定の環境ではおそらく最速です。さまざまなアーキテクチャーを条件付けする方法を示すことは、非常に役立ちます。
fearless_fool

1

これは最速でも最短でもありませんが、とても読みやすいと思います。だから私はこのようなことをします:

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

バイナリは2の累乗に基づいているため、これは機能します。1ビットのみが設定された数値は、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の3項演算子でさえも、本当にこだわりたい場合はより明確になりintます。
luk32

0

私はこれが非常に古い投稿であることを知っていますが、ここに投稿するのは面白いかもしれないと思いました。


コード-ゴルフSE:(これを書いた1(S)への信用すべてのように)言語のショーケース

Cに関する段落、サブ段落の長さ36の抜粋

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

-1

もう1つの方法(おそらく最速ではないかもしれません)は、ln(x)/ ln(2)が整数かどうかを判断することです。


2
多分それについてはありません:-)。
paxdiablo 2008

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

対数を4回行うよりもはるかに高速です(最初のセットは10進数の結果を取得するため、2番目のセットは整数のセットを取得して比較するため)


5
この質問に対するトップアンサーをT-SQLに実装する方法を確認するのは良いことですが、これは、ここで質問する質問にはあまり関係ありません。別の方法(T-SQLでソリューションを探していて、この回答された質問を見つけ、T-SQLに実装し、この回答を投稿するのに十分興味深いと考えた場合)は、T-SQLを参照して質問を投稿することです。この回答された質問を参照して、自分で回答してください。この提案がお役に立てば幸いです。
サイモン

これは実際にはこの質問の答えにはなりません
phuclv 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.