użycie operatorów bitowych do spakowania wielu wartości w jednej int
Niskopoziomowa manipulacja bitami nigdy nie była moją mocną stroną. Będę wdzięczny za pomoc w zrozumieniu następującego przypadku użycia operatorów bitowych.Zastanów się...
int age, gender, height, packed_info;
. . . // Assign values
// Pack as AAAAAAA G HHHHHHH using shifts and "or"
packed_info = (age << 8) | (gender << 7) | height;
// Unpack with shifts and masking using "and"
height = packed_info & 0x7F; // This constant is binary ...01111111
gender = (packed_info >> 7) & 1;
age = (packed_info >> 8);
Nie jestem pewien, co ten kod osiąga i jak? Po co używać magicznej liczby 0x7F ? Jak przebiega pakowanie i rozpakowywanie?
7 answers
Jak mówi komentarz, spakujemy wiek, płeć i wzrost do 15 bitów formatu:
AAAAAAAGHHHHHHH
Zacznijmy od tej części:
(age << 8)
Na początek, wiek ma taki format:
age = 00000000AAAAAAA
Gdzie każde A może być 0 LUB 1.
<< 8
przenosi bity 8 miejsc w lewo i wypełnia luki zerami. Więc masz:
(age << 8) = AAAAAAA00000000
Podobnie:
gender = 00000000000000G
(gender << 7) = 0000000G0000000
height = 00000000HHHHHHH
Teraz chcemy połączyć je w jedną zmienną. Operator |
działa przez patrząc na każdy bit i zwracając 1, jeśli bit jest 1 na którymś z wejść. Więc:
0011 | 0101 = 0111
Jeśli bit jest 0 Na jednym wejściu, to bit jest pobierany z drugiego wejścia. Patrząc na (age << 8)
, (gender << 7)
i height
, zobaczysz, że jeśli bit jest 1 dla jednego z nich, to jest 0 dla innych. Więc:
packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH
Teraz chcemy rozpakować bity. Zacznijmy od wzrostu. Chcemy uzyskać ostatnie 7 bitów i zignorować pierwsze 8. W tym celu używamy operatora &
, który zwraca 1 tylko wtedy, gdy oba bitów wejściowych wynosi 1. Więc:
0011 & 0101 = 0001
Więc:
packed_info = AAAAAAAGHHHHHHH
0x7F = 000000001111111
(packed_info & 0x7F) = 00000000HHHHHHH = height
Aby uzyskać wiek, możemy po prostu wcisnąć wszystko 8 miejsc w prawo, a my zostajemy z 0000000AAAAAAAA
. Więc age = (packed_info >> 8)
.
Wreszcie, aby uzyskać płeć, wciskamy wszystko 7 Miejsc w prawo, aby pozbyć się wzrostu. Wtedy zależy nam tylko na ostatnim bitie:
packed_info = AAAAAAAGHHHHHHH
(packed_info >> 7) = 0000000AAAAAAAG
1 = 000000000000001
(packed_info >> 7) & 1 = 00000000000000G
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 12:37:41
To może być dość długa lekcja manipulacji bitami, ale najpierw pozwól, że wskażę ci również artykuł o maskowaniu bitów na Wikipedii .
packed_info = (age << 8) | (gender << 7) | height;
Weź wiek i przesuń jego wartość o 8 bitów, następnie weź płeć i przesuń ją o 7 bitów, a wysokość zajmie ostatnie bity.
age = 0b101
gender = 0b1
height = 0b1100
packed_info = 0b10100000000
| 0b00010000000
| 0b00000001100
/* which is */
packed_info = 0b10110001100
Rozpakowywanie działa odwrotnie, ale używa masek takich jak 0x7F (czyli 0b 01111111) do przycinania innych wartości w polu.
gender = (packed_info >> 7) & 1;
Będzie działać jak...
gender = 0b1011 /* shifted 7 here but still has age on the other side */
& 0b0001
/* which is */
gender = 0b1
Zauważ, że cokolwiek 1 jest tym samym co" zachowanie "tego bitu, A 0 jest tym samym co" ignorowanie " tego bitu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 12:33:57
Gdybyś miał zapisać datę jako liczbę, może osiągnąłbyś ją mnożąc rok przez 10000, miesiąc przez 100 i dodając dzień. Data taka jak 2 lipca 2011 będzie zakodowana jako liczba 20110702:
year * 10000 + month * 100 + day -> yyyymmdd
2011 * 10000 + 7 * 100 + 2 -> 20110702
Możemy powiedzieć, że zakodowaliśmy datę w masce yyyymmdd. Możemy opisać tę operację jako
- Przesuń rok o 4 pozycje w lewo,
- przesuń pozycje miesiąca 2 w lewo i
- zostaw dzień tak, jak jest.
- następnie połącz trzy wartości razem.
To jest to samo, co dzieje się z kodowaniem wieku, płci i wzrostu, tyle że autor myśli binarnie.
Zobacz zakresy, które mogą mieć te wartości:
age: 0 to 127 years
gender: M or F
height: 0 to 127 inches
Jeśli przetłumaczymy te wartości na binarne, otrzymamy to:
age: 0 to 1111111b (7 binary digits, or bits)
gender: 0 or 1 (1 bit)
height: 0 to 1111111b (7 bits also)
Mając to na uwadze, możemy zakodować dane wiek-płeć-wzrost za pomocą maski aaaaaaghhhhhhhhhhh [9]}, tyle tylko, że tutaj mówimy o binarne cyfry, Nie dziesiętne cyfry.
Więc,
- Przesuń wiek 8 bity w lewo,
- przesuń płeć 7 bitów w lewo i
- pozostaw wysokość tak, jak jest.
- następnie połącz wszystkie trzy wartości razem.
W systemie binarnym operator Shift-Left ( n w lewo. Operator" lub " ("|"w wielu językach) łączy wartości razem. Dlatego:
(age << 8) | (gender << 7) | height
Jak "dekodować" te wartości?
Jest łatwiej w systemie binarnym niż dziesiętnym:
- "maskujesz" wysokość,
- przesuń płeć 7 bitów w prawo i zamaskuj to również i na koniec
- przesuń wiek 8 bitów w prawo.
Operator Shift-Right (>>) przesuwa wartość n pozycji w prawo (dowolne cyfry przesunięte "na zewnątrz" z prawej pozycji są tracone). Operator binarny" i " ("&"w wielu języki) maskuje bity. Aby to zrobić, potrzebuje maski, wskazującej, które bity należy zachować, a które zniszczyć (1 bity są zachowane). Dlatego:
height = value & 1111111b (preserve the 7 rightmost bits)
gender = (value >> 1) & 1 (preserve just one bit)
age = (value >> 8)
Ponieważ 1111111b w hex jest 0x7f w większości języków, to jest powód tej magicznej liczby. Taki sam efekt uzyskasz używając 127 (czyli 1111111b w układzie dziesiętnym).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 14:01:19
Bardziej skondensowana odpowiedź:
AAAAAA G HHHHHHH
Pakowanie:
packed = age << 8 | gender << 7 | height
Alternatywnie możesz po prostu zsumować komponenty, jeśli ie jest używane w MySQL SUM agregate function
packed = age << 8 + gender << 7 + height
rozpakowywanie:
age = packed >> 8 // no mask required
gender = packed >> 7 & ((1 << 1) - 1) // applying mask (for gender it is just 1)
height = packed & ((1 << 7) - 1) // applying mask
Kolejny (dłuższy) przykład:
Powiedzmy, że masz adres IP, który chcesz spakować, jednak jest to fikcyjny adres IP, np. 132.513.151.319. Zauważ, że niektóre elementy większe niż 256, które wymaga więcej niż 8 bitów w przeciwieństwie do rzeczywistych adresów ip.
Najpierw musimy dowiedzieć się, jakiego offsetu musimy użyć, aby móc zapisać maksymalną liczbę. Powiedzmy, że w przypadku naszych fikcyjnych adresów IP żaden komponent nie może być większy niż 999, co oznacza, że potrzebujemy 10 bitów pamięci na komponent (pozwala na liczby do 1014).
packed = (comp1 << 0 * 10) | (comp1 << 1 * 10) | (comp1 << 2 * 10) | (comp1 << 3 * 10)
Co daje dec 342682502276
lub bin 100111111001001011110000000010010000100
Teraz rozpakujmy wartość
comp1 = (packed >> 0 * 10) & ((1 << 10) - 1) // 132
comp2 = (packed >> 1 * 10) & ((1 << 10) - 1) // 513
comp3 = (packed >> 2 * 10) & ((1 << 10) - 1) // 151
comp4 = (packed >> 3 * 10) & ((1 << 10) - 1) // 319
Gdzie (1 << 10) - 1
jest maską binarną, której używamy do ukrywania bitów po lewej stronie poza 10 prawymi większością bitów jesteśmy zainteresowani.
Ten sam przykład przy użyciu zapytania MySQL
SELECT
(@offset := 10) AS `No of bits required for each component`,
(@packed := (132 << 0 * @offset) |
(513 << 1 * @offset) |
(151 << 2 * @offset) |
(319 << 3 * @offset)) AS `Packed value (132.513.151.319)`,
BIN(@packed) AS `Packed value (bin)`,
(@packed >> 0 * @offset) & ((1 << @offset) - 1) `Component 1`,
(@packed >> 1 * @offset) & ((1 << @offset) - 1) `Component 2`,
(@packed >> 2 * @offset) & ((1 << @offset) - 1) `Component 3`,
(@packed >> 3 * @offset) & ((1 << @offset) - 1) `Component 4`;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-17 23:44:53
Lewy operator przesunięcia oznacza "mnożenie przez dwa, tyle razy". W systemie binarnym mnożenie liczby przez dwa jest tym samym, co dodawanie zera do prawej strony.
Operator zmiany prawej jest odwrotnością operatora zmiany lewej.
Operatorem rury jest "or", co oznacza nakładanie dwóch liczb binarnych na siebie, a jeśli w każdej z nich jest 1, wynik w tej kolumnie wynosi 1.
Więc wyodrębnijmy operację dla packed_info:
// Create age, shifted left 8 times:
// AAAAAAA00000000
age_shifted = age << 8;
// Create gender, shifted left 7 times:
// 0000000G0000000
gender_shifted = gender << 7;
// "Or" them all together:
// AAAAAAA00000000
// 0000000G0000000
// 00000000HHHHHHH
// ---------------
// AAAAAAAGHHHHHHH
packed_info = age_shifted | gender_shifted | height;
I rozpakowywanie jest odwrotne.
// Grab the lowest 7 bits:
// AAAAAAAGHHHHHHH &
// 000000001111111 =
// 00000000HHHHHHH
height = packed_info & 0x7F;
// right shift the 'height' bits into the bit bucket, and grab the lowest 1 bit:
// AAAAAAAGHHHHHHH
// >> 7
// 0000000AAAAAAAG &
// 000000000000001 =
// 00000000000000G
gender = (packed_info >> 7) & 1;
// right shift the 'height' and 'gender' bits into the bit bucket, and grab the result:
// AAAAAAAGHHHHHHH
// >> 8
// 00000000AAAAAAA
age = (packed_info >> 8);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 12:40:26
Możesz zobaczyć wyrażenie x & mask
jako operację, która usuwa z x
bity, które nie są obecne (tzn. mają wartość 0) w mask
. Oznacza to, że packed_info & 0x7F
usuwa z packed_info
wszystkie bity znajdujące się powyżej siódmego bitu.
Przykład: Jeśli packed_info
jest 1110010100101010
w systemie binarnym, to packed_info & 0x7f
będzie
1110010100101010
0000000001111111
----------------
0000000000101010
Tak więc w height
otrzymujemy dolne 7 bitów packed_info
.
Następnie przesuwamy całość packed_info
o 7, w ten sposób usuwamy informacje, które już odczytaliśmy. Więc dostajemy (dla wartości z poprzedniego przykładu) 111001010
płeć jest przechowywana w następnym bitie, więc tą samą sztuczką: & 1
wydobywamy tylko ten bit z informacji. Reszta informacji znajduje się na offsecie 8.
Pakowanie z powrotem nie jest też skomplikowane: bierzesz age
, przesuwasz go o 8 bitów (więc dostajesz 1110010100000000
z 11100101
), przesuwasz gender
o 7 (więc dostajesz 00000000
) i bierzesz wysokość (zakładając, że zmieściłaby się niższa 7 bitów). Następnie komponujesz je wszystkie razem:
1110010100000000
0000000000000000
0000000000101010
----------------
1110010100101010
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 12:34:34
To samo Wymaganie, z jakim miałem do czynienia wiele razy. Jest to bardzo łatwe za pomocą bitowego i operatora. Wystarczy zakwalifikować swoje wartości z rosnącymi mocami dwóch (2). Aby zapisać wiele wartości, dodaj ich liczbę względną (moc 2) i uzyskaj sumę. Suma ta skonsoliduje wybrane wartości. Jak ?
Po Prostu Zrób bitowe i z każdą wartością, a da zero (0) dla wartości, które nie zostały wybrane i niezerowe dla których są wybrane.
Oto explanation:
1) wartości ( tak, nie, może)
2) przypisanie do mocy dwóch (2)
YES = 2^0 = 1 = 00000001
NO = 2^1 = 2 = 00000010
MAYBE = 2^2 = 4 = 00000100
3) wybieram tak i może stąd suma:
SUM = 1 + 4 = 5
SUM = 00000001 + 00000100 = 00000101
Ta wartość będzie przechowywać zarówno tak, jak i może. Jak?
1 & 5 = 1 ( non zero )
2 & 5 = 0 ( zero )
4 & 5 = 4 ( non zero )
Stąd suma składa się z
1 = 2^0 = YES
4 = 2^2 = MAYBE.
Aby uzyskać bardziej szczegółowe wyjaśnienie i wdrożenie odwiedź mój blog
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-19 16:08:04