Rozważ ten kod:
x = 1 # 0001
x << 2 # Shift left 2 bits: 0100
# Result: 4
x | 2 # Bitwise OR: 0011
# Result: 3
x & 1 # Bitwise AND: 0001
# Result: 1
Rozumiem operatory arytmetyczne w Pythonie (i innych językach), ale nigdy nie rozumiałem zbyt dobrze operatorów „bitowych”. W powyższym przykładzie (z książki o Pythonie) rozumiem lewą zmianę, ale nie rozumiem dwóch pozostałych.
Do czego służą operatory bitowe? Byłbym wdzięczny za kilka przykładów.
Odpowiedzi:
Operatory bitowe to operatory, które działają na wartościach wielobitowych, ale koncepcyjnie po jednym bicie.
AND
ma wartość 1 tylko wtedy, gdy oba wejścia mają wartość 1, w przeciwnym razie wynosi 0.OR
wynosi 1, jeśli jedno lub oba jego wejścia mają wartość 1, w przeciwnym razie wynosi 0.XOR
wynosi 1 tylko wtedy , gdy dokładnie jedno z jego wejść ma wartość 1, w przeciwnym razie wynosi 0.NOT
ma wartość 1 tylko wtedy, gdy jego wejście wynosi 0, w przeciwnym razie wynosi 0.Można je najlepiej przedstawić jako tabele prawdy. Możliwości wejściowe są na górze i po lewej stronie, wynikowy bit jest jedną z czterech (dwóch w przypadku NIE, ponieważ ma tylko jedno wejście) wartości pokazanych na przecięciu wejść.
Jednym z przykładów jest to, że jeśli chcesz tylko 4 dolne bity liczby całkowitej, to ORAZ to z 15 (binarne 1111), więc:
Bity zerowe w 15 w tym przypadku skutecznie działają jak filtr, zmuszając bity wyniku również do zera.
Ponadto
>>
i<<
często są uwzględniane jako operatory bitowe i „przesuwają” wartość odpowiednio w prawo i w lewo o określoną liczbę bitów, odrzucając bity, które toczą się na końcu, do którego się przesuwasz, i wprowadzając zero bitów na drugi koniec.Na przykład:
Zwróć uwagę, że przesunięcie w lewo w Pythonie jest niezwykłe, ponieważ nie używa stałej szerokości, w której bity są odrzucane - podczas gdy wiele języków używa stałej szerokości w oparciu o typ danych, Python po prostu rozszerza szerokość, aby obsłużyć dodatkowe bity. Aby uzyskać zachowanie odrzucania w Pythonie, możesz śledzić przesunięcie w lewo z bitowym przesunięciem, na
and
przykład w 8-bitowym przesunięciu wartości w lewo o cztery bity:Mając to na uwadze, kolejny przykład operatorów bitowe jest, jeśli masz dwie wartości 4-bitowe, które chcesz zapakować do jednej z 8-bitowym, można użyć wszystkich trzech swoich operatorów (
left-shift
,and
ior
):& 15
Operacja będzie upewnić się, że obie wartości mają jedynie niższe 4 bity.<< 4
4-bitowe przesunięcie w lewo, aby przejśćval1
do pierwszych 4 bitów wartości 8-bitowej.|
prostu łączy te dwa razem.Jeśli
val1
wynosi 7 ival2
4:źródło
Jedno typowe użycie:
|
służy do ustawiania pewnego bitu na 1&
służy do testowania lub usuwania określonego fragmentuUstaw bit (gdzie n to numer bitu, a 0 to najmniej znaczący bit):
unsigned char a |= (1 << n);
Wyczyść trochę:
unsigned char b &= ~(1 << n);
Przełącz trochę:
unsigned char c ^= (1 << n);
Przetestuj trochę:
unsigned char e = d & (1 << n);
Weźmy na przykład przypadek Twojej listy:
x | 2
służy do ustawiania bitu 1x
na 1x & 1
służy do sprawdzania, czy bit 0 ofx
to 1 czy 0źródło
Jednym z najczęstszych zastosowań operacji bitowych jest analizowanie kolorów szesnastkowych.
Na przykład, oto funkcja Pythona, która akceptuje ciąg znaków typu String
#FF09BE
i zwraca krotkę jego wartości Red, Green i Blue.Wiem, że są bardziej wydajne sposoby, aby to osiągnąć, ale uważam, że jest to naprawdę zwięzły przykład ilustrujący zarówno przesunięcia, jak i bitowe operacje logiczne.
źródło
Myślę, że druga część pytania:
Został tylko częściowo rozwiązany. To są moje dwa centy w tej sprawie.
Operacje bitowe w językach programowania odgrywają podstawową rolę w przypadku wielu aplikacji. Prawie wszystkie obliczenia niskiego poziomu muszą być wykonywane przy użyciu tego rodzaju operacji.
We wszystkich aplikacjach, które wymagają przesyłania danych między dwoma węzłami, takich jak:
sieć komputerowa;
aplikacje telekomunikacyjne (telefony komórkowe, komunikacja satelitarna itp.).
W warstwie komunikacji niższego poziomu dane są zwykle przesyłane w tzw . Ramkach . Ramki to po prostu ciągi bajtów przesyłane kanałem fizycznym. Te ramki zazwyczaj zawierają rzeczywiste dane oraz kilka innych pól (zakodowanych w bajtach), które są częścią tego, co nazywa się nagłówkiem . Nagłówek zwykle zawiera bajty, które kodują pewne informacje związane ze stanem komunikacji (np. Z flagami (bitami)), licznikami ramek, kodami korekcji i wykrywania błędów itp. Aby uzyskać przesyłane dane w ramce i zbudować ramki do wysyłania danych, na pewno będziesz potrzebować operacji bitowych.
Ogólnie rzecz biorąc, gdy mamy do czynienia z tego rodzaju aplikacjami, dostępne jest API, więc nie trzeba zajmować się wszystkimi tymi szczegółami. Na przykład wszystkie współczesne języki programowania udostępniają biblioteki do połączeń przez gniazdo, więc w rzeczywistości nie trzeba budować ramek komunikacyjnych TCP / IP. Ale pomyśl o dobrych ludziach, którzy zaprogramowali dla ciebie te interfejsy API, na pewno mieli do czynienia z konstrukcją ram; używanie wszelkiego rodzaju operacji bitowych do przechodzenia między komunikacją niskiego poziomu a komunikacją wyższego poziomu.
Jako konkretny przykład wyobraź sobie, że ktoś daje ci plik zawierający surowe dane, które zostały przechwycone bezpośrednio przez sprzęt telekomunikacyjny. W takim przypadku, aby znaleźć ramki, będziesz musiał odczytać surowe bajty w pliku i spróbować znaleźć jakieś słowa synchronizujące, skanując dane bit po bicie. Po zidentyfikowaniu słów synchronizujących będziesz musiał pobrać rzeczywiste ramki i PRZESUNIĄĆ je, jeśli to konieczne (a to dopiero początek historii), aby uzyskać rzeczywiste dane, które są przesyłane.
Inną bardzo odmienną rodziną aplikacji niskiego poziomu jest sytuacja, w której trzeba sterować sprzętem za pomocą (pewnego rodzaju starożytnych) portów, takich jak porty równoległe i szeregowe. Te porty są kontrolowane przez ustawienie niektórych bajtów, a każdy bit z tych bajtów ma określone znaczenie, jeśli chodzi o instrukcje, dla tego portu (patrz na przykład http://en.wikipedia.org/wiki/Parallel_port ). Jeśli chcesz zbudować oprogramowanie, które robi coś z tym sprzętem, będziesz potrzebować operacji bitowych, aby przetłumaczyć instrukcje, które chcesz wykonać, na bajty zrozumiałe dla portu.
Na przykład, jeśli masz kilka fizycznych przycisków podłączonych do portu równoległego, aby sterować innym urządzeniem, jest to wiersz kodu, który można znaleźć w aplikacji:
Mam nadzieję, że to się przyczyni.
źródło
Mam nadzieję, że to wyjaśnia te dwa:
źródło
x & 1
nie ilustruje efektu tak dobrze, jakx & 2
by to było.Pomyśl o 0 jako fałszywym i 1 jako o prawdziwym. Następnie bitowe i (&) i lub (|) działają tak samo jak zwykłe i i lub z wyjątkiem tego, że wykonują wszystkie bity wartości naraz. Zazwyczaj zobaczysz je używane jako flagi, jeśli masz 30 opcji, które można ustawić (np. Jako style rysowania w oknie), nie chcesz przekazywać 30 oddzielnych wartości boolowskich, aby ustawić lub usunąć każdą z nich, więc używasz | aby połączyć opcje w jedną wartość, a następnie użyj &, aby sprawdzić, czy każda opcja jest ustawiona. Ten styl przekazywania flag jest intensywnie używany przez OpenGL. Ponieważ każdy bit jest oddzielną flagą, otrzymujesz wartości flag na potęgach dwóch (czyli liczbach, które mają tylko jeden ustawiony bit) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) potęga dwóch mówi, który bit jest ustawiony, jeśli flaga jest włączona.
Zwróć także uwagę na 2 = 10, więc x | 2 to 110 (6), a nie 111 (7) Jeśli żaden z bitów nie nakłada się (co jest prawdą w tym przypadku) | działa jak dodawanie.
źródło
Nie widziałem tego wspomnianego powyżej, ale zobaczysz również, że niektórzy ludzie używają lewego i prawego przesunięcia do operacji arytmetycznych. Przesunięcie w lewo o x jest równoważne pomnożeniu przez 2 ^ x (o ile się nie przepełnia), a przesunięcie w prawo jest równoważne podzieleniu przez 2 ^ x.
Ostatnio widziałem ludzi używających x << 1 i x >> 1 do podwojenia i zmniejszenia o połowę, chociaż nie jestem pewien, czy po prostu starają się być sprytni, czy naprawdę mają wyraźną przewagę nad zwykłymi operatorami.
źródło
Zestawy
Zbiory można łączyć za pomocą operacji matematycznych.
|
sumy łączy dwa zestawy, aby utworzyć nowy zawierający elementy w jednym z nich.&
pobiera elementy tylko w obu.-
pobiera elementy w pierwszym zestawie, ale nie w drugim.^
pobiera elementy w obu zestawach, ale nie w obu.Spróbuj sam:
Wynik:
źródło
Ten przykład pokaże operacje dla wszystkich czterech wartości 2-bitowych:
Oto jeden przykład użycia:
źródło
Innym częstym przypadkiem użycia jest manipulowanie / testowanie uprawnień do plików. Zobacz moduł statystyczny Pythona: http://docs.python.org/library/stat.html .
Na przykład, aby porównać uprawnienia pliku z żądaną polisą uprawnień, możesz zrobić coś takiego:
Wyniki oceniam jako wartości logiczne, ponieważ obchodzi mnie tylko prawda lub fałsz, ale warto byłoby wydrukować wartości bin () dla każdego z nich.
źródło
not bool((mode ^ desired_mode) & 0777)
. Lub (łatwiejsze do zrozumienia)not (mode & 0777) ^ desired_mode == 0
. AND pozostawi tylko interesujące bity, XOR sprawdzi, jakie wszystkie żądane bity są ustawione. Jawne== 0
porównanie jest bardziej znaczące niżbool()
.setWindowFlags
. Przykład:setWindowFlags(SplashScreen | WindowStaysOnTopHint)
. Nadal uważam to za mylące, ponieważ wydaje się, że przełącznik, który ustawiasz jako „włączony”, wydaje się bardziej intuicyjny w przypadku „i” w takim przypadku.Bitowe reprezentacje liczb całkowitych są często używane w obliczeniach naukowych do reprezentowania tablic informacji prawda-fałsz, ponieważ operacja bitowa jest znacznie szybsza niż iterowanie przez tablicę wartości logicznych. (Języki wyższego poziomu mogą wykorzystywać koncepcję tablicy bitowej).
Ładnym i dość prostym przykładem tego jest ogólne rozwiązanie w grze Nim. Spójrz na kod Pythona na stronie Wikipedii . To sprawia, że intensywne użytkowanie wyłącznych mnożenie lub,
^
.źródło
Może istnieć lepszy sposób na znalezienie miejsca, w którym element tablicy znajduje się między dwiema wartościami, ale jak pokazuje ten przykład, znak & działa tutaj, podczas gdy i nie.
źródło
nie widziałem tego wspomnianego, ten przykład pokaże operację dziesiętną (-) dla wartości 2-bitowych: AB (tylko jeśli A zawiera B)
ta operacja jest potrzebna, gdy trzymamy w naszym programie czasownik, który reprezentuje bity. czasami musimy dodać bity (jak powyżej), a czasami musimy usunąć bity (jeśli czasownik zawiera wtedy)
z pythonem: 7 i ~ 4 = 3 (usuń z 7 bity reprezentujące 4)
z pythonem: 1 & ~ 4 = 1 (usuń z 1 bity reprezentujące 4 - w tym przypadku 1 nie oznacza „zawiera” 4).
źródło
Chociaż manipulowanie bitami liczby całkowitej jest przydatne, często w przypadku protokołów sieciowych, które mogą być określone z dokładnością do bitu, można wymagać manipulacji dłuższymi sekwencjami bajtów (które nie są łatwo przekształcane w jedną liczbę całkowitą). W tym przypadku przydatne jest użycie biblioteki bitstring, która pozwala na operacje bitowe na danych - np. Można zaimportować ciąg 'ABCDEFGHIJKLMNOPQ' jako ciąg znaków lub jako szesnastkowo-bitowy przesuwać go (lub wykonywać inne operacje bitowe):
źródło
następujące operatory bitowe: & , | , ^ i ~ zwracają wartości (na podstawie ich danych wejściowych) w ten sam sposób, w jaki bramki logiczne wpływają na sygnały. Możesz ich użyć do emulacji obwodów.
źródło
Aby odwrócić bity (tj. Uzupełnienie / odwrócenie 1), możesz wykonać następujące czynności:
Ponieważ wartość ExORed ze wszystkimi 1s skutkuje inwersją, dla danej szerokości bitowej można użyć ExOR do ich odwrócenia.
źródło