Chociaż istnieje wiele sposobów odwrócenia kolejności bitów w bajcie, jestem ciekawy, który z nich jest „najprostszy” do zaimplementowania przez programistę. I odwracając mam na myśli:
1110 -> 0111
0010 -> 0100
To jest podobne do tego pytania PHP , ale nie jest jego duplikatem .
To jest podobne do tego pytania , ale nie jest jego duplikatem . To pytanie dotyczy najłatwiejszej metody do wdrożenia przez programistę. „Najlepszy algorytm” dotyczy pamięci i wydajności procesora.
c++
c
bit-manipulation
nathan
źródło
źródło
Odpowiedzi:
Jeśli mówisz o pojedynczym bajcie, prawdopodobnie najlepszym rozwiązaniem jest przeszukiwanie tabeli, chyba że z jakiegoś powodu nie masz dostępnych 256 bajtów.
źródło
unsigned int rtable[] = {0x800, 0x4000, ...};
. Następnie wyrzuć scenariusz i zapomnij, że go kiedykolwiek miałeś. Pisanie jest znacznie szybsze niż odpowiadający mu kod w C ++ i będzie działał tylko raz, więc otrzymujesz środowisko uruchomieniowe O (1) w kodzie C ++.To powinno działać:
Najpierw lewe cztery bity są zamieniane na prawe cztery bity. Następnie zamieniane są wszystkie sąsiednie pary, a następnie wszystkie sąsiednie pojedyncze bity. Powoduje to odwróconą kolejność.
źródło
Myślę, że tablica przeglądowa musi być jedną z najprostszych metod. Nie potrzebujesz jednak pełnej tabeli odnośników.
To dość proste do zakodowania i weryfikacji wizualnej.
Ostatecznie może to być nawet szybsze niż pełny stół. Bitowa arytmia jest tania, a tabela łatwo mieści się w wierszu pamięci podręcznej.
źródło
b0001(1) -> b1000(0x8)
,b0010(2) -> b0100(0x4)
,b1010(10) -> b0101(0x5)
. Widzisz wzór? Jest to na tyle proste, że możesz to obliczyć w głowie (jeśli potrafisz czytać binarnie, w przeciwnym razie będziesz potrzebować papieru, aby to obliczyć). Jeśli chodzi o skok, to odwrócenie 8-bitowej liczby całkowitej jest tym samym, co odwrócenie 4-bitowych części, a następnie ich zamiana; Mam doświadczenie i intuicję (lub magię).Zobacz trochę dziwacznych hacków dla wielu rozwiązań. Tworzenie kopii z tego miejsca jest oczywiście łatwe do zaimplementowania. =)
Na przykład (na 32-bitowym procesorze):
Jeśli przez „łatwe do wdrożenia” oznacza się coś, co można zrobić bez referencji na egzaminie lub rozmowie kwalifikacyjnej, to najbezpieczniejszym rozwiązaniem jest prawdopodobnie nieefektywne kopiowanie bitów jeden po drugim do innej zmiennej w odwrotnej kolejności (już pokazane w innych odpowiedziach ).
źródło
Ponieważ nikt nie opublikował pełnego rozwiązania do wyszukiwania tabel, oto moje:
źródło
EDYTOWAĆ:
Przekonwertowano go na szablon z opcjonalną liczbą bitcount
źródło
sizeof(T)*8
zsizeof(T)*CHAR_BITS
.sizeof(T)*CHAR_BIT
nastd::numeric_limits<T>::digits
(prawie 4 lata pedanterii później).CHAR_BIT
, nieCHAR_BITS
.Dwie linie:
lub w przypadku problemów z częścią „0b1”:
„oryginalny” to bajt, który chcesz cofnąć. „odwrócony” to wynik zainicjowany na 0.
źródło
Chociaż prawdopodobnie nie przenośny, użyłbym języka asemblera.
Wiele języków asemblera ma instrukcje, aby obrócić nieco flagę przeniesienia i obrócić flagę przeniesienia do słowa (lub bajtu).
Algorytm to:
Kod języka wysokiego poziomu jest znacznie bardziej skomplikowany, ponieważ C i C ++ nie obsługują rotacji do przenoszenia i rotacji do przenoszenia. Flaga nośna musi być wymodelowana.
Edycja: na przykład język asemblera
źródło
Uważam, że poniższe rozwiązanie jest prostsze niż inne bitowe algorytmy, które tu widziałem.
Pobiera każdy bit w bajcie i odpowiednio go przesuwa, zaczynając od pierwszego do ostatniego.
Wyjaśnienie:
źródło
Plik Najprostszym sposobem jest prawdopodobnie iteracyjne nad pozycjach bitowych w pętli:
źródło
CHAR_BIT
Po co używać, skoro zakładasz, żechar
masz 8 bitów?Możesz być zainteresowany
std::vector<bool>
(to jest bitowy) istd::bitset
Powinien być najprostszy zgodnie z żądaniem.
Inne opcje mogą być szybsze.
EDYCJA: Jestem ci winien rozwiązanie wykorzystujące
std::vector<bool>
Drugi przykład wymaga rozszerzenia c ++ 0x (aby zainicjować tablicę
{...}
). Zaleta używania abitset
lub astd::vector<bool>
(lub aboost::dynamic_bitset
) jest to, że nie jesteś ograniczony do bajtów lub słów, ale możesz odwrócić dowolną liczbę bitów.HTH
źródło
std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend());
- bezpośrednie użycie odwrotnych iteratorów?W bardzo ograniczonym przypadku stałego 8-bitowego wejścia ta metoda nie kosztuje pamięci ani procesora w czasie wykonywania:
Użyłem tego dla ARINC-429, gdzie kolejność bitów (endianness) etykiety jest przeciwna do reszty słowa. Etykieta jest często stała i zwykle jest ósemkowa.
Oto jak użyłem go do zdefiniowania stałej, ponieważ specyfikacja definiuje tę etykietę jako big-endian 205 ósemkową.
Więcej przykładów:
źródło
Istnieje wiele sposobów odwracania bitów, w zależności od tego, co masz na myśli w „najprostszy sposób”.
Odwróć przez rotację
Chyba najbardziej logiczne jest obrócenie bajtu z nałożeniem maski na pierwszy bit
(n & 1)
:1) Ponieważ długość znaku unsigner wynosi 1 bajt, co jest równe 8 bitom, oznacza to, że przeskanujemy każdy bit
while (byte_len--)
2) Najpierw sprawdzamy, czy b jest trochę na skrajnej prawej stronie
(b & 1)
; jeśli tak, ustawiamy bit 1 na r z|
i przesuwamy go o 1 bit w lewo, mnożąc r przez 2 przez(r << 1)
3) Następnie dzielimy znak b bez znaku b przez 2,
b >>=1
aby usunąć bit znajdujący się po prawej stronie zmiennej b. Przypominamy, że b >> = 1; jest równoważne b / = 2;Odwróć w jednej linii
To rozwiązanie jest przypisywane Richowi Schroeppelowi w sekcji Programming Hacks
1) Operacja mnożenia (b * 0x0202020202ULL) tworzy pięć oddzielnych kopii 8-bitowego wzorca bajtowego do rozłożenia na 64-bitową wartość.
2) Operacja AND (& 0x010884422010ULL) wybiera bity, które znajdują się we właściwych (odwróconych) pozycjach względem każdej 10-bitowej grupy bitów.
3) Razem operacje mnożenia i AND kopiują bity z oryginalnego bajtu, więc każdy z nich pojawia się tylko w jednym z 10-bitowych zestawów. Odwrócone pozycje bitów z oryginalnego bajtu pokrywają się z ich względnymi pozycjami w ramach dowolnego zestawu 10-bitowego.
4) Ostatni krok (% 0x3ff), który polega na dzieleniu modułu przez 2 ^ 10 - 1, skutkuje scaleniem każdego zestawu 10 bitów (z pozycji 0-9, 10-19, 20-29, ...) w wartości 64-bitowej. Nie nakładają się, więc kroki dodawania leżące u podstaw dzielenia modułu zachowują się jak operacje OR.
Dziel i zwyciężaj
To jest najbardziej pozytywna odpowiedź i pomimo pewnych wyjaśnień myślę, że dla większości ludzi trudno jest sobie wyobrazić, co naprawdę oznacza 0xF0, 0xCC, 0xAA, 0x0F, 0x33 i 0x55.
Nie korzysta z '0b', które jest rozszerzeniem GCC i jest dołączone od standardu C ++ 14, wydanego w grudniu 2014, a więc chwilę po tej odpowiedzi z kwietnia 2010
Sprawdź poniższe fragmenty kodu, aby zapamiętać i lepiej zrozumieć to rozwiązanie, w którym poruszamy się o połowę:
NB: Dzieje się
>> 4
tak, ponieważ w 1 bajcie jest 8 bitów, co jest znakiem bez znaku, więc chcemy wziąć drugą połowę i tak dalej.Moglibyśmy łatwo zastosować to rozwiązanie do 4 bajtów z tylko dwoma dodatkowymi wierszami i zgodnie z tą samą logiką. Ponieważ obie maski się uzupełniają, możemy nawet użyć ~, aby zmienić bity i zaoszczędzić trochę atramentu:
[Tylko C ++] Odwróć wszystkie niepodpisane (szablon)
Powyższą logikę można podsumować pętlą, która działałaby na dowolnym typie bez znaku:
Spróbuj sam z włączeniem powyższej funkcji:
Odwróć z asm volatile
Wreszcie, jeśli najprostszy oznacza mniej linii, dlaczego nie spróbować montażu na linii?
Możesz przetestować poniższy fragment kodu, dodając
-masm=intel
podczas kompilacji:Objaśnienia wiersz po wierszu:
źródło
Wyszukiwanie w tabeli lub
edytować
Spójrz tutaj dla innych rozwiązań, które mogłyby działać lepiej dla Ciebie
źródło
wolniejsza, ale prostsza implementacja:
źródło
Czy to może być szybkie rozwiązanie?
Eliminuje konieczność korzystania z pętli for! ale eksperci proszę mi powiedzieć, czy to jest wydajne i szybsze?
źródło
Przed wdrożeniem jakiegokolwiek rozwiązania algorytmicznego sprawdź język asemblera dla dowolnej architektury procesora, którego używasz. Twoja architektura może zawierać instrukcje obsługujące takie manipulacje bitowe (a co może być prostsze niż pojedyncza instrukcja asemblera?).
Jeśli taka instrukcja nie jest dostępna, sugerowałbym skorzystanie z trasy tabeli przeglądowej. Możesz napisać skrypt / program, który wygeneruje tabelę dla Ciebie, a operacje wyszukiwania będą szybsze niż którykolwiek z algorytmów odwracania bitów tutaj (kosztem konieczności przechowywania gdzieś tabeli przeglądowej).
źródło
Ta prosta funkcja używa maski do testowania każdego bitu w bajcie wejściowym i przenoszenia go na przesuwające się wyjście:
źródło
Ta jedna jest oparta na jednym BobStein-VisiBone przewidzianym
Bardzo mi się to podoba, ponieważ kompilator automatycznie obsługuje pracę za Ciebie, więc nie wymaga żadnych dodatkowych zasobów.
można to również rozszerzyć do 16-bitów ...
źródło
b
w nawiasach na wypadek, gdyby było to bardziej złożone wyrażenie niż pojedyncza liczba, a może również zmienić nazwę makra naREVERSE_BYTE
wskazówkę, że prawdopodobnie nie chcesz mieć tam bardziej złożonego (uruchomieniowego) wyrażenia. Lub uczyń to funkcją wbudowaną. (Ale ogólnie podoba mi się to jako na tyle proste, że można to łatwo zrobić z pamięci z bardzo małą szansą na błąd.)Zakładając, że Twój kompilator zezwala na długi bez znaku :
Odkryto tutaj
źródło
Jeśli używasz małego mikrokontrolera i potrzebujesz szybkiego rozwiązania o małej powierzchni, mogą to być rozwiązania. Można go użyć do projektu C, ale musisz dodać ten plik jako plik assemblera * .asm, do swojego projektu w C. Instrukcje: W projekcie C dodaj tę deklarację:
Wywołaj tę funkcję z C
To jest kod, nadaje się tylko do rdzenia 8051. W rejestrze procesora r0 znajdują się dane z byteInput . Kod obróć w prawo r0 cross carry, a następnie obróć w lewo do r1 . Powtórz tę procedurę 8 razy dla każdego bitu. Następnie rejestr r1 jest zwracany do funkcji c jako byteOutput. W rdzeniu 8051 można obracać tylko akumulator a .
PLUSY: Jest mały, jest szybki WADY: Nie jest to kod wielokrotnego użytku, dotyczy tylko 8051
011101101-> nosić
101101110 <-carry
źródło
odwrócony bajt będzie w rejestrze bl
źródło
źródło
uint8_t
pól 1-bitowych jest trochę brzydkie, ponieważ wydaje się, że najpierw mówi, że zajmie 8 bitów, ale na końcu linii definiuje to jako tylko jeden bit. Chciałbym używaćunsigned b0:1
itd.źródło
Myślę, że to dość proste
lub
źródło
źródło
Wrzucę swoje rozwiązanie, ponieważ jak dotąd nie mogę znaleźć czegoś takiego w odpowiedziach. Może jest trochę przeprojektowany, ale generuje tablicę przeglądową przy użyciu C ++ 14
std::index_sequence
w czasie kompilacji.https://godbolt.org/z/cSuWhF
źródło
Oto proste i czytelne rozwiązanie, przenośne na wszystkie zgodne platformy, w tym te z
sizeof(char) == sizeof(int)
:źródło
Wiem, że to pytanie jest przestarzałe, ale nadal uważam, że temat jest istotny do pewnych celów, a oto wersja, która działa bardzo dobrze i jest czytelna. Nie mogę powiedzieć, że jest najszybszy lub najbardziej wydajny, ale powinien być jednym z najczystszych. Dołączyłem również funkcję pomocniczą do łatwego wyświetlania wzorów bitowych. Ta funkcja używa niektórych standardowych funkcji bibliotecznych zamiast pisania własnego manipulatora bitowego.
I daje następujący wynik.
źródło
Ten pomógł mi z zestawem tablic z matrycą punktową 8x8.
źródło