Was ist der einfachste Weg, um zu testen, ob eine Zahl in C ++ eine Zweierpotenz ist?

90

Ich brauche eine Funktion wie diese:

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

Kann mir jemand vorschlagen, wie ich das schreiben könnte? Können Sie mir eine gute Website nennen, auf der diese Art von Algorithmus zu finden ist?

Ameise
quelle
@rootTraveller - Wahrscheinlich kein Duplikat. C ++ und Java sind unterschiedliche Sprachen und bieten jeweils unterschiedliche Funktionen. In C / C ++ können wir jetzt beispielsweise Intrinsics mit BMI-fähigen Prozessoren verwenden, die die Maschinenanweisung ausgeben, um dies in einem Takt zu tun. Ich stelle mir vor, Java hat andere Dinge, wie vielleicht etwas aus einer Mathe-Routine.
JWW

Antworten:

183

(n & (n - 1)) == 0ist das Beste. Beachten Sie jedoch, dass für n = 0 fälschlicherweise true zurückgegeben wird. Wenn dies möglich ist, sollten Sie dies explizit überprüfen.

http://www.graphics.stanford.edu/~seander/bithacks.html verfügt über eine große Sammlung cleverer Bit-Twiddling-Algorithmen, einschließlich dieses.

Anonym
quelle
7
(n>0 && ((n & (n-1)) == 0))
Also im
1
@ SaurabhGoyal oder n && !(n & (n - 1))als Link innerhalb der Antwort angegeben.
Carsten
Warum, oh warum, steht das nicht ganz oben auf den Antworten? OP bitte akzeptieren.
Donturner
@ SaurabhGoyal Eine kleine Verbesserung ist : n & !(n & (n - 1)). Beachten Sie das bitweise UND &(nicht logisch und &&). Bitweise Operatoren implementieren keinen Kurzschluss und daher verzweigt der Code nicht. Dies ist in Situationen vorzuziehen, in denen Zweigfehlvorhersagen wahrscheinlich sind und wenn die Berechnung der Rhs des Ausdrucks (dh !(n & (n - 1))) billig ist.
Cassio Neri
@cassio !ist ein logischer Operator und daher wäre der Wert von !(n & (n - 1))ein Boolescher Wert. Sind Sie sicher, dass einem bitweisen UND-Operator ein Boolescher Wert und eine Zahl zugewiesen werden können? Wenn ja, sieht es gut aus.
Saurabh Goyal
81

Bei einer Zweierpotenz wird nur ein Bit gesetzt (für vorzeichenlose Zahlen). Etwas wie

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

Wird gut funktionieren; Eins weniger als eine Zweierpotenz ist alles 1s in den weniger signifikanten Bits, muss also bitweise UND auf 0 sein.

Da ich vorzeichenlose Zahlen angenommen habe, ist der == 0-Test (den ich ursprünglich vergessen habe, sorry) ausreichend. Möglicherweise möchten Sie einen Test> 0, wenn Sie vorzeichenbehaftete Ganzzahlen verwenden.

Adam Wright
quelle
Ihnen fehlt ein '!' oder eine '== 0'
Sie vermissen auch einen Test für den negativen Wert von x.
Rob Wells
Ordentlich, wie haben Sie es bearbeitet, ohne dass "Vor x Minuten bearbeitet" angezeigt wurde?
Im Ernst, wie haben Sie gerade 120 Wiederholungen für eine nachweislich falsche Antwort bekommen?
@ Mike F: In der Tat scheinen die Leute über Antworten abzustimmen, ohne sie zu überprüfen. Ich denke, jeder kann einen Fehler machen - wenn ich in Zukunft einen mache, können Sie ihn jederzeit bearbeiten.
Adam Wright
48

Zweierpotenzen in Binärform sehen folgendermaßen aus:

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

Beachten Sie, dass immer genau 1 Bit gesetzt ist. Die einzige Ausnahme ist eine vorzeichenbehaftete Ganzzahl. Beispiel: Eine 8-Bit-Ganzzahl mit Vorzeichen und einem Wert von -128 sieht folgendermaßen aus:

10000000

Nachdem wir überprüft haben, ob die Zahl größer als Null ist, können wir mit einem cleveren kleinen Bit-Hack testen, ob nur ein Bit gesetzt ist.

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

Weitere Informationen finden Sie hier .

Matt Howells
quelle
14

Ansatz 1:

Teilen Sie die Zahl ausschließlich durch 2, um sie zu überprüfen.

Zeitliche Komplexität: O (log2n).

Ansatz 2:

Bitweise UND die Zahl mit der gerade vorherigen Zahl sollte gleich NULL sein.

Beispiel: Zahl = 8 Binär von 8: 1 0 0 0 Binär von 7: 0 1 1 1 und das bitweise UND beider Zahlen ist 0 0 0 0 = 0.

Zeitliche Komplexität: O (1).

Ansatz 3:

Bitweises XOR Die Zahl mit der gerade vorherigen Zahl sollte die Summe beider Zahlen sein.

Beispiel: Zahl = 8 Binär von 8: 1 0 0 0 Binär von 7: 0 1 1 1 und das bitweise XOR beider Zahlen ist 1 1 1 1 = 15.

Zeitliche Komplexität: O (1).

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

Raj Dixit
quelle
8
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}
Rob Wells
quelle
7

Für jede Potenz von 2 gilt auch das Folgende.

n & (- n) == n

HINWEIS: Die Bedingung gilt für n = 0, obwohl es keine Potenz von 2 ist.
Grund dafür ist:
-n ist das 2s-Komplement von n. -n wird jedes Bit links vom am weitesten rechts gesetzten Bit von n im Vergleich zu n umgedreht haben. Für Potenzen von 2 gibt es nur ein gesetztes Bit.

FReeze FRancis
quelle
2
Ich meinte, die Bedingung ist wahr für n = 0, obwohl es keine Zweierpotenz ist
FReeze FRancis
Funktioniert dies mit den Konvertierungen, die auftreten, wenn n nicht signiert ist?
Joseph Garvin
5

In C ++ 20 gibt es eine, std::ispow2die Sie genau für diesen Zweck verwenden können, wenn Sie sie nicht selbst implementieren müssen:

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

Dies ist wahrscheinlich die schnellste, wenn Sie GCC verwenden. Es wird nur eine POPCNT-CPU-Anweisung und ein Vergleich verwendet. Bei der binären Darstellung einer Potenz mit 2 Zahlen wird immer nur ein Bit gesetzt, andere Bits sind immer Null. Wir zählen also die Anzahl der gesetzten Bits mit POPCNT, und wenn es gleich 1 ist, ist die Zahl die Potenz von 2. Ich glaube, es gibt keine möglichen schnelleren Methoden. Und es ist sehr einfach, wenn Sie es einmal verstanden haben:

if(1==__builtin_popcount(n))
F10PPY
quelle
Nee. Ich habe das gerade getestet. Ich liebe Popcount, aber für den Power-of-2-Test ist der Test i && !(i & (i - 1)))auf meinem Computer etwa 10% schneller, selbst wenn ich sicher war, den POPCNT-Befehl für native Assemblys in gcc zu aktivieren.
Ära
Ups, ich nehme es zurück. Mein Testprogramm lief in einer Schleife und die Verzweigungsvorhersage war "Betrug". Sie haben Recht, wenn Sie die POPCNT-Anweisung auf Ihrer CPU haben, ist es schneller.
Ära
3

Das Folgen wäre aufgrund des booleschen Kurzschlusses und der Tatsache, dass der Vergleich langsam ist, schneller als die am häufigsten abgestimmte Antwort.

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

Wenn Sie wissen, dass x nicht 0 sein kann, dann

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x  1));
}
Margus
quelle
3
return n > 0 && 0 == (1 << 30) % n;
Yuxiang Zhang
quelle
3

Was ist der einfachste Weg, um zu testen, ob eine Zahl in C ++ eine Zweierpotenz ist?

Wenn Sie einen modernen Intel-Prozessor mit den Bit-Manipulationsanweisungen haben , können Sie Folgendes ausführen. Der reine C / C ++ - Code wird weggelassen, da andere ihn bereits beantwortet haben. Sie benötigen ihn jedoch, wenn der BMI nicht verfügbar oder aktiviert ist.

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 und Clang signalisieren BMI-Unterstützung mit __BMI__. Es ist in Microsoft Compilern in Visual Studio 2015 und höher verfügbar, wenn AVX2 verfügbar und aktiviert ist . Informationen zu den benötigten Headern finden Sie unter Header-Dateien für SIMD-Eigenheiten .

Normalerweise bewache ich das _blsr_u64mit einem _LP64_Fall, der auf i686 kompiliert wird. Clang benötigt eine kleine Problemumgehung, da ein etwas anderer intrinsischer Symbolname verwendet wird:

#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

Können Sie mir eine gute Website nennen, auf der diese Art von Algorithmus zu finden ist?

Diese Website wird oft zitiert: Bit Twiddling Hacks .

jww
quelle
Dies ist sicherlich nicht der "einfachste Weg", wie er im OP gefordert wird, aber wohl der schnellste für bestimmte Umgebungen. Es ist äußerst nützlich zu zeigen, wie für verschiedene Architekturen konditioniert werden kann.
fearless_fool
1

Dies ist nicht der schnellste oder kürzeste Weg, aber ich denke, er ist sehr gut lesbar. Also würde ich so etwas machen:

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

Dies funktioniert, da Binär auf Zweierpotenzen basiert. Jede Zahl mit nur einem gesetzten Bit muss eine Zweierpotenz sein.

Jere.Jones
quelle
Es mag nicht schnell oder kurz sein, aber es ist im Gegensatz zu den Top-Antworten korrekt.
2
Zum Zeitpunkt des Kommentierens waren sie alle abgehört. Sie wurden inzwischen in einen akzeptablen Zustand gebracht.
0

Hier ist eine andere Methode, in diesem Fall |anstelle von &:

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

Es ist möglich durch c ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}
Jay Ponkia
quelle
2
Das ist für mich weder einfach noch schnell.
luk32
2
Das heißt, es ist sicherlich nicht schnell log2, und der Beweis, dass es funktioniert, ist nicht so einfach zu erklären (genau, können Sie von Rundungsfehlern erwischt werden?). Es ist auch unnötig verworren mit if..return..else..return. Was ist falsch daran, es zu kollabieren return x==(double)y;? Es sollte boolsowieso zurückgeben. IMO sogar ternäre Operator wäre klarer, wenn man wirklich bleiben will int.
luk32
0

Ich weiß, dass dies ein sehr alter Beitrag ist, aber ich dachte, es könnte interessant sein, dies hier zu posten.


Von Code-Golf SE (also alle Ehre gebührt denjenigen, die dies geschrieben haben): Showcase of Languages

(Absatz über C , Unterabsatz Länge 36 Ausschnitt )

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

Ein anderer Weg (vielleicht nicht der schnellste) besteht darin, festzustellen, ob ln (x) / ln (2) eine ganze Zahl ist.

MikeJ
quelle
2
Es gibt vielleicht kein Problem :-).
Paxdiablo
1
Dies hätte Probleme mit der Gleitkomma-Ungenauigkeit. ln (1 << 29) / ln (2) ergibt 29.000000000000004.
Anonym
-3

Dies ist die Bitverschiebungsmethode in T-SQL (SQL Server):

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

Es ist viel schneller als viermal einen Logarithmus zu erstellen (erster Satz, um ein Dezimalergebnis zu erhalten, zweiter Satz, um eine Ganzzahl zu erhalten und zu vergleichen)

Jesse Roberge
quelle
5
Es ist gut zu sehen, wie die Top-Antwort auf diese Frage auch in T-SQL implementiert werden kann, aber das ist für die hier gestellte Frage nicht wirklich relevant. Eine Alternative (wenn Sie nach einer Lösung in T-SQL suchen, diese beantwortete Frage finden, in T-SQL implementieren und es interessant genug finden, diese Antwort zu veröffentlichen) wäre, die Frage unter Bezugnahme auf T-SQL zu veröffentlichen Beantworten Sie es selbst unter Bezugnahme auf diese beantwortete Frage. Hoffe dieser Vorschlag ist hilfreich.
Simon
Dies beantwortet diese Frage nicht wirklich
phuclv