Ich möchte eine Funktion schreiben, die die nächste Potenz von 2 Zahlen zurückgibt. Wenn meine Eingabe beispielsweise 789 ist, sollte die Ausgabe 1024 sein. Gibt es eine Möglichkeit, dies zu erreichen, ohne Schleifen zu verwenden, sondern nur einige bitweise Operatoren zu verwenden?
c
optimization
bit-manipulation
Naveen
quelle
quelle
Antworten:
Überprüfen Sie die Bit Twiddling Hacks . Sie müssen den Logarithmus zur Basis 2 erhalten und dann 1 hinzufügen. Beispiel für einen 32-Bit-Wert:
Die Erweiterung auf andere Breiten sollte offensichtlich sein.
quelle
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
Und für 32 Bit:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
Wenn Sie GCC verwenden (und Clang, denke ich?), Ist es ratsam , sich die Zeit dafür zu nehmen Suchen Sie den Aufruf von CLZ, anstatt alle Optionen zu kopieren und einzufügen.x > UINT32_MAX
keine Verzweigung vorliegt. Außerdem werden GCC und Clang-mtune=generic
standardmäßig verwendet (wie die meisten Distributionen), sodass Ihr Code NICHT auf dielzcnt
Anweisung in x86_64 erweitert wird - er wird tatsächlich auf etwas VIEL Langsameres erweitert (eine libgcc-Routine), es sei denn, Sie verwenden so etwas-march=native
. Ihr vorgeschlagener Ersatz ist also nicht portabel, fehlerhaft und (normalerweise) langsamer.Dies funktioniert, indem Sie die Zahl finden, um die Sie 2 erhöhen würden, um x zu erhalten (nehmen Sie das Protokoll der Zahl und dividieren Sie es durch das Protokoll der gewünschten Basis, siehe Wikipedia für weitere Informationen ). Runden Sie das dann mit Ceil ab, um die nächste ganze Zahl zu erhalten.
Dies ist eine allgemeinere (dh langsamere!) Methode als die an anderer Stelle verknüpften bitweisen Methoden, aber gut, um die Mathematik zu kennen, oder?
quelle
log(pow(2,29))/log(2)
= 29.000000000000004, das Ergebnis ist also 2 30, anstatt 2 29 zurückzugeben. Ich denke, aus diesem Grund gibt es log2-Funktionen?quelle
uint32_t
.Ich denke, das funktioniert auch:
Und die Antwort ist
power
.quelle
power <<= 1
x
zu groß ist (dh nicht genügend Bits, um die nächste Potenz von 2 darzustellen).Wenn Sie GCC verwenden, sollten Sie sich die Optimierung der next_pow2 () -Funktion von Lockless Inc. ansehen. Auf dieser Seite wird eine Möglichkeit beschrieben, die integrierte Funktion
builtin_clz()
(Anzahl führender Nullpunkte) und später direkt x86 (ia32) zu verwenden. Assemblerbefehlbsr
(Bit - Scan - Reverse), so wie es in beschrieben ist eine andere Antwort ‚s Link zu gamedev Website . Dieser Code ist möglicherweise schneller als die in der vorherigen Antwort beschriebenen .Übrigens, wenn Sie keine Assembler-Anweisung und keinen 64-Bit-Datentyp verwenden möchten, können Sie diese verwenden
quelle
_BitScanForward
auf Visual C ++__builtin_ctz()
__builtin_ctz()
wird nicht nützlich sein, um eine Nicht-Potenz von 2 auf die nächste Potenz von zwei zu rundenconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Eins mehr, obwohl ich Zyklus benutze, aber das ist viel schneller als mathematische Operanden
Power of Two "Floor" Option:
Potenz von zwei "Ceil" -Option:
AKTUALISIEREN
Wie in den Kommentaren erwähnt, gab es einen Fehler, bei
ceil
dem das Ergebnis falsch war.Hier sind alle Funktionen:
quelle
x
die Leistung 2 ist. Ein Mikro zum Testen, ob der Eingang eine Leistung von 2 ist, wird benötigt.#define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
power of two "ceil" option
ist nicht richtig. Zum Beispiel, wennx = 2
das Ergebnis2
anstelle von4
Aufbauend auf den Bit Twiddling Hacks für jeden nicht signierten Typ:
Es gibt dort keine wirkliche Schleife, da der Compiler zur Kompilierungszeit die Anzahl der Iterationen kennt.
quelle
std::is_unsigned<UnsignedType>::value
Behauptung zu beachten.Für IEEE-Floats können Sie so etwas tun.
Wenn Sie eine Ganzzahllösung benötigen und die Inline-Assembly verwenden können, gibt Ihnen BSR das Protokoll2 einer Ganzzahl auf dem x86. Es wird gezählt, wie viele richtige Bits gesetzt sind, was genau dem log2 dieser Zahl entspricht. Andere Prozessoren haben (häufig) ähnliche Anweisungen, wie z. B. CLZ. Abhängig von Ihrem Compiler steht möglicherweise eine eigene Anleitung zur Verfügung, um die Arbeit für Sie zu erledigen.
quelle
Trotz der Frage ist wie
c
hier meine fünf Cent markiert . Glücklicherweise würde C ++ 20std::ceil2
und enthaltenstd::floor2
(siehe hier ). Es handelt sich umconsexpr
Vorlagenfunktionen, die aktuelle GCC-Implementierung verwendet Bitshifting und funktioniert mit jedem integralen vorzeichenlosen Typ.quelle
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf umbenanntWenn Sie sich nicht in den Bereich undefinierten Verhaltens wagen möchten, muss der Eingabewert zwischen 1 und 2 ^ 63 liegen. Das Makro ist auch nützlich, um zur Kompilierungszeit eine Konstante festzulegen.
quelle
Der Vollständigkeit halber ist hier eine Gleitkomma-Implementierung in Moor-Standard C.
quelle
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
ist etwa 25x schneller.Eine effiziente Microsoft-spezifische (z. B. Visual Studio 2017) spezifische Lösung in C / C ++ für die Eingabe von Ganzzahlen. Behandelt den Fall, dass der Eingang genau mit einer Zweierpotenz übereinstimmt, indem er dekrementiert wird, bevor die Position des höchstwertigen 1-Bits überprüft wird.
Dies erzeugt ungefähr 5 Inline-Anweisungen für einen Intel-Prozessor, ähnlich den folgenden:
Anscheinend ist der Visual Studio C ++ - Compiler nicht codiert, um dies für Werte zur Kompilierungszeit zu optimieren, aber es ist nicht so, dass es dort eine ganze Reihe von Anweisungen gibt.
Bearbeiten:
Wenn Sie möchten, dass ein Eingabewert von 1 1 ergibt (2 zur nullten Potenz), wird durch eine kleine Änderung des obigen Codes immer noch direkt Anweisungen ohne Verzweigung generiert.
Generiert nur noch ein paar Anweisungen. Der Trick besteht darin, dass Index durch einen Test gefolgt von einer cmove-Anweisung ersetzt werden kann.
quelle
In x86 können Sie die Anweisungen zur Manipulation von sse4-Bits verwenden, um es schnell zu machen.
In c können Sie die passenden Intrinsics verwenden.
quelle
Hier ist meine Lösung in C. Hoffe das hilft!
quelle
Viele Prozessorarchitekturen unterstützen
log base 2
oder sehr ähnliche Operationen -count leading zeros
. Viele Compiler haben Eigenheiten dafür. Siehe https://en.wikipedia.org/wiki/Find_first_setquelle
Vorausgesetzt, Sie haben einen guten Compiler und er kann das bisschen vor der Hand drehen, das an diesem Punkt über mir liegt, aber trotzdem funktioniert das !!!
Testcode unten:
Ausgänge:
quelle
Ich versuche, die nächst niedrigere Potenz von 2 zu erhalten und habe diese Funktion ausgeführt. Möge es Ihnen helfen. Multiplizieren Sie einfach die nächste untere Zahl mit 2, um die nächste obere Potenz von 2 zu erhalten
quelle
Angepasst an Paul Dixons Antwort auf Excel funktioniert dies perfekt.
quelle
Eine Variante der @ YannDroneaud-Antwort gilt
x==1
nur für x86-Plattenformen, Compiler, gcc oder clang:quelle
Hier ist, was ich verwende, damit dies ein konstanter Ausdruck ist, wenn die Eingabe ein konstanter Ausdruck ist.
So zum Beispiel ein Ausdruck wie:
wird schön auf eine Konstante reduzieren.
quelle
Die folgende Klarstellung kann für Ihren Zweck hilfreich sein:
quelle
Konvertieren Sie es in einen Float und verwenden Sie dann .hex (), das die normalisierte IEEE-Darstellung zeigt.
>>> float(789).hex() '0x1.8a80000000000p+9'
Dann extrahieren Sie einfach den Exponenten und addieren 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
Und erhöhe 2 auf diese Kraft.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
quelle
quelle
Wenn Sie es für OpenGL-bezogene Dinge benötigen:
quelle
Wenn Sie eine einzeilige Vorlage möchten. Hier ist es
oder
quelle
n
Mehrfaches Ändern ohne Sequenzpunkt ist ungültig. Sie haben es so geschrieben, als obn-=1
es zuerst passieren sollte, aber die einzige Garantie hier ist, dass esn
seinen neuen Wert enthält, nachdem;
und die Klammern dies nicht ändern.