Chcę napisać funkcję, która zwraca najbliższą następną potęgę liczby 2. Na przykład, jeśli mój sygnał wejściowy to 789, wynik powinien wynosić 1024. Czy jest jakiś sposób na osiągnięcie tego bez użycia żadnych pętli, a jedynie z wykorzystaniem operatorów bitowych?
c
optimization
bit-manipulation
Naveen
źródło
źródło
Odpowiedzi:
Sprawdź Bit Twiddling Hacks . Musisz uzyskać logarytm podstawowy 2, a następnie dodać 1 do tego. Przykład wartości 32-bitowej:
Rozszerzenie na inne szerokości powinno być oczywiste.
źródło
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
I dla wersji 32-bitowej:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
To znaczy, jeśli używasz GCC (i chyba Clanga?), Ale rozsądnie byłoby poświęcić czas na znajdź połączenie do CLZ zamiast kopiowania i wklejania wszystkich dostępnych opcji.x > UINT32_MAX
nie jest bez rozgałęzienia. Ponadto GCC i Clang używają-mtune=generic
domyślnie (podobnie jak większość dystrybucji), więc twój kod NIE będzie rozwijał się dolzcnt
instrukcji na x86_64 - w rzeczywistości rozwinie się do DUŻO wolniejszego (procedura libgcc), chyba że użyjesz czegoś takiego-march=native
. Tak więc proponowana zamiana jest nieprzenośna, zawiera błędy i (zazwyczaj) jest wolniejsza.Działa to poprzez znalezienie liczby, o którą podniesionobyś 2, aby uzyskać x (weź dziennik liczby i podziel przez dziennik żądanej bazy, więcej informacji na stronie wikipedia ). Następnie zaokrąglij to w górę, aby uzyskać najbliższą liczbę całkowitą.
Jest to metoda bardziej ogólnego przeznaczenia (tj. Wolniejsza!) Niż metody bitowe powiązane gdzie indziej, ale dobrze jest znać matematykę, co?
źródło
log(pow(2,29))/log(2)
= 29,000000000000004, więc wynik to 2 30 zamiast zwracania 2 29. Myślę, że właśnie dlatego istnieją funkcje log2?źródło
uint32_t
.Myślę, że to też działa:
A odpowiedź jest
power
.źródło
power <<= 1
x
jest za duża (tj. Za mało bitów, aby przedstawić następną potęgę 2).Jeśli używasz GCC, możesz rzucić okiem na Optymalizowanie funkcji next_pow2 () przez Lockless Inc .. Ta strona opisuje sposób korzystania z funkcji wbudowanej
builtin_clz()
(liczenie zera wiodącego), a później bezpośredniego używania x86 (ia32) instrukcja asemblerabsr
(reverse nieco scan), tak jak to opisano w innym odpowiedź „s link miejscu GameDev . Ten kod może być szybszy niż opisany w poprzedniej odpowiedzi .Nawiasem mówiąc, jeśli nie zamierzasz używać instrukcji asemblera i 64-bitowego typu danych, możesz tego użyć
źródło
_BitScanForward
w Visual C ++__builtin_ctz()
__builtin_ctz()
nie przyda się zaokrąglić żadną potęgę 2 liczby do następnej potęgi dwóchconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Jeszcze jeden, chociaż używam cyklu, ale jest on znacznie szybszy niż operandy matematyczne
moc dwóch opcji „podłogowych”:
moc dwóch opcji „sufitu”:
AKTUALIZACJA
Jak wspomniano w komentarzach, błąd polegał na tym,
ceil
że jego wynik był błędny.Oto pełne funkcje:
źródło
x
jest mocą 2. Potrzebny jest mikro do sprawdzenia, czy moc wejściowa wynosi 2.#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
to nie jest poprawne. Na przykład, kiedyx = 2
wynik powinien być2
zamiast4
W przypadku dowolnego niepodpisanego typu, opierając się na Bit Twiddling Hacks:
Tak naprawdę nie ma tam pętli, ponieważ kompilator zna w czasie kompilacji liczbę iteracji.
źródło
std::is_unsigned<UnsignedType>::value
stwierdzenie.Dla pływaków IEEE możesz zrobić coś takiego.
Jeśli potrzebujesz rozwiązania liczb całkowitych i możesz użyć wbudowanego zestawu, BSR da ci log2 liczby całkowitej na x86. Zlicza, ile odpowiednich bitów jest ustawionych, co jest dokładnie równe log2 tej liczby. Inne procesory mają podobne instrukcje (często), takie jak CLZ, a w zależności od kompilatora może istnieć nieodłączny element do wykonania pracy za Ciebie.
źródło
Pomimo tego pytania jest oznaczone jako
c
moje pięć centów. Na szczęście, C ++ 20 będzie zawieraćstd::ceil2
istd::floor2
(patrz tutaj ). Są toconsexpr
funkcje szablonów, obecna implementacja GCC wykorzystuje przesunięcie bitów i działa z dowolnym zintegrowanym typem bez znaku.źródło
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfJeśli nie chcesz zapuszczać się w sferę nieokreślonego zachowania, wartość wejściowa musi wynosić od 1 do 2 ^ 63. Makro jest również przydatne do ustawienia stałej w czasie kompilacji.
źródło
Dla kompletności oto implementacja zmiennoprzecinkowa w standardzie torfowiska C.
źródło
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
jest około 25 razy szybszy.Wydajne rozwiązanie specyficzne dla Microsoft (np. Visual Studio 2017) w C / C ++ dla wprowadzania liczb całkowitych. Obsługuje przypadek wejścia dokładnie dopasowującego potęgę dwóch wartości poprzez zmniejszenie przed sprawdzeniem lokalizacji najbardziej znaczącego 1 bitu.
To generuje 5 lub mniej instrukcji dla procesora Intel podobnego do następującego:
Najwyraźniej kompilator Visual Studio C ++ nie jest zakodowany w celu zoptymalizowania tego pod kątem wartości czasu kompilacji, ale nie jest tak, że zawiera wiele instrukcji.
Edytować:
Jeśli chcesz, aby wartość wejściowa 1 dawała 1 (2 do mocy zerowej), niewielka modyfikacja powyższego kodu nadal generuje bezpośrednie instrukcje bez rozgałęzienia.
Generuje tylko kilka instrukcji. Sztuka polega na tym, że indeks można zastąpić testem, a następnie instrukcją cmove.
źródło
W x86 możesz użyć instrukcji manipulacji bitem sse4, aby przyspieszyć.
W c można użyć pasujących elementów wewnętrznych.
źródło
Oto moje rozwiązanie w C. Mam nadzieję, że to pomaga!
źródło
Wiele architektur procesorów obsługuje
log base 2
lub bardzo podobne operacje -count leading zeros
. Wiele kompilatorów ma w tym coś wewnętrznego. Zobacz https://en.wikipedia.org/wiki/Find_first_setźródło
Zakładając, że masz dobry kompilator i to może zrobić trochę kręcenie przed ręką, która jest nade mną w tym momencie, ale i tak to działa !!!
Kod testowy poniżej:
Wyjścia:
źródło
Próbuję uzyskać najbliższą niższą moc 2 i włączyć tę funkcję. Może ci to pomóc. Wystarczy pomnożyć najbliższą dolną liczbę razy 2, aby uzyskać najbliższą górną potęgę 2
źródło
Dostosowana odpowiedź Paula Dixona do Excela działa idealnie.
źródło
Odmiana odpowiedzi @YannDroneaud jest ważna dla
x==1
, tylko dla platform x86, kompilatorów, gcc lub clang:źródło
Oto, czego używam, aby mieć to stałe wyrażenie, jeśli dane wejściowe są stałym wyrażeniem.
Na przykład wyrażenie takie jak:
ładnie zredukuje się do stałej.
źródło
Pomocne w osiągnięciu celu może być następujące wyjaśnienie:
źródło
Konwertuj go na liczbę zmiennoprzecinkową, a następnie użyj .hex (), który pokazuje znormalizowaną reprezentację IEEE.
>>> float(789).hex() '0x1.8a80000000000p+9'
Następnie wyodrębnij wykładnik potęgi i dodaj 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
I podnieś 2 do tej mocy.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
źródło
źródło
Jeśli potrzebujesz go do rzeczy związanych z OpenGL:
źródło
Jeśli chcesz szablon z jedną linią. Oto jest
lub
źródło
n
Wielokrotne modyfikowanie bez punktu sekwencji jest nieprawidłowe. Napisałeś to tak, jakby ton-=1
miało być pierwsze, ale jedyną gwarancją jest to, żen
zawiera nową wartość po,;
a nawiasy nie zmieniają tego.