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?

Źródło

Author: Maksim Yegorov, 2011-07-02

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
 58
Author: thomson_matt,
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.

 9
Author: Andrew White,
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).

 4
Author: Branco Medeiros,
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`;
 3
Author: Alexei Tenitski,
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);
 2
Author: Anarchofascist,
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
 1
Author: Vlad,
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

 0
Author: user5212481,
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