Szukaj…


Uwagi

Operacje przesunięcia bitów nie są przenośne we wszystkich architekturach procesorów, różne procesory mogą mieć różne szerokości bitów. Innymi słowy, jeśli napisałeś

int a = ~0;
int b = a << 1;

Ta wartość byłaby inna na komputerze 64-bitowym niż na komputerze 32-bitowym lub z procesora opartego na x86 na procesorze opartym na PIC.

Endianowość nie musi być brana pod uwagę przy samych operacjach bitowych, to znaczy, prawe przesunięcie ( >> ) spowoduje przesunięcie bitów w kierunku najmniej znaczącego bitu, a XOR wykona wyłączność lub na bitach. Endianowość musi być brana pod uwagę tylko w przypadku samych danych, to znaczy, jeśli endianność jest problemem dla twojej aplikacji, jest to problem niezależnie od nieco mądrzejszych operacji.

& - bitowe AND

int a = 6;     // 0110b  (0x06)
int b = 10;    // 1010b  (0x0A)
int c = a & b; // 0010b  (0x02)

std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;

Wynik

a = 6, b = 10, c = 2

Dlaczego

Nieco mądry AND działa na poziomie bitowym i wykorzystuje następującą logiczną tabelę prawdy:

TRUE  AND TRUE  = TRUE
TRUE  AND FALSE = FALSE
FALSE AND FALSE = FALSE

Kiedy wartość binarna dla a ( 0110 ) i wartość binarna dla b ( 1010 ) są AND razem, otrzymujemy wartość binarną 0010 :

int a = 0 1 1 0
int b = 1 0 1 0 &
        ---------
int c = 0 0 1 0

Bitowe mądre AND nie zmienia wartości oryginalnych wartości, chyba że przypisano je konkretnie do użycia operatora złożonego bitowego przypisania &= :

int a = 5;  // 0101b  (0x05)
a &= 10;    // a = 0101b & 1010b

| - bitowe OR

int a = 5;     // 0101b  (0x05)
int b = 12;    // 1100b  (0x0C)
int c = a | b; // 1101b  (0x0D)

std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;

Wynik

a = 5, b = 12, c = 13

Dlaczego

Nieco mądra operacja OR działa na poziomie bitowym i wykorzystuje następującą logiczną tabelę prawdy:

true OR true = true
true OR false = true
false OR false = false

Gdy wartość binarna dla a ( 0101 ) i wartość binarna dla b ( 1100 ) są OR połączone, otrzymujemy wartość binarną 1101 :

int a = 0 1 0 1
int b = 1 1 0 0 |
        ---------
int c = 1 1 0 1

Bitowe mądre OR nie zmienia wartości oryginalnych wartości, chyba że przypisano je konkretnie do używania operatora złożonego bitowego przypisania |= :

int a = 5;  // 0101b  (0x05)
a |= 12;    // a = 0101b | 1101b

^ - bitowy XOR (wyłączny OR)

int a = 5;     // 0101b  (0x05)
int b = 9;     // 1001b  (0x09)
int c = a ^ b; // 1100b  (0x0C)

std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;

Wynik

a = 5, b = 9, c = 12

Dlaczego

Nieco mądre XOR (wyłączne lub) działa na poziomie bitowym i wykorzystuje następującą logiczną tabelę prawdy:

true OR true = false
true OR false = true
false OR false = false

Zauważ, że w przypadku operacji XOR true OR true = false gdzie jak w przypadku operacji true AND/OR true = true , stąd wyłączny charakter operacji XOR.

Korzystając z tego, gdy wartość binarna dla a ( 0101 ) i wartość binarna dla b ( 1001 ) są razem XOR , otrzymujemy wartość binarną 1100 :

int a = 0 1 0 1
int b = 1 0 0 1 ^
        ---------
int c = 1 1 0 0

Bitowy XOR nie zmienia wartości pierwotnych wartości, chyba że jest przypisany konkretnie do użycia operatora złożonego przypisania bitowego ^= :

int a = 5;  // 0101b  (0x05)
a ^= 9;    // a = 0101b ^ 1001b

Mało bitowy XOR może być wykorzystywany na wiele sposobów i często jest wykorzystywany w operacjach maski bitowej do szyfrowania i kompresji.

Uwaga: Poniższy przykład jest często przedstawiany jako przykład dobrej sztuczki. Ale nie powinien być stosowany w kodzie produkcyjnym (istnieją lepsze sposoby std::swap() aby osiągnąć ten sam wynik).

Możesz także użyć operacji XOR, aby zamienić dwie zmienne bez tymczasowego:

int a = 42;
int b = 64;

// XOR swap
a ^= b;
b ^= a;
a ^= b;

std::cout << "a = " << a << ", b = " << b << "\n";

Aby to zrobić, musisz dodać czek, aby upewnić się, że można go użyć.

void doXORSwap(int& a, int& b)
{
    // Need to add a check to make sure you are not swapping the same
    // variable with itself. Otherwise it will zero the value.
    if (&a != &b)
    {
        // XOR swap
        a ^= b;
        b ^= a;
        a ^= b;
    }
}

Więc choć wygląda to na fajną sztuczkę w izolacji, nie jest użyteczny w prawdziwym kodzie. xor nie jest podstawową operacją logiczną, ale kombinacją innych: a ^ c = ~ (a & c) i (a | c)

również w kompilatorach 2015+ zmienne można przypisać jako binarne:

int cn=0b0111;

~ - bitowe NIE (jednoargumentowe uzupełnienie)

unsigned char a = 234;  // 1110 1010b  (0xEA)
unsigned char b = ~a;   // 0001 0101b  (0x15)

std::cout << "a = " << static_cast<int>(a) <<
             ", b = " << static_cast<int>(b) << std::endl;

Wynik

a = 234, b = 21

Dlaczego

Nieco mądre NOT (jednoargumentowe uzupełnienie) działa na poziomie bitów i po prostu odwraca każdy bit. Jeśli jest to 1 , zmienia się na 0 , jeśli jest to 0 , zmienia się na 1 . Mądrze NIE ma takiego samego efektu jak XOR'owanie wartości względem wartości maksymalnej dla określonego typu:

unsigned char a = 234;  // 1110 1010b  (0xEA)
unsigned char b = ~a;   // 0001 0101b  (0x15)
unsigned char c = a ^ ~0;

Nieco mądry może być również wygodnym sposobem sprawdzenia maksymalnej wartości dla określonego typu całki:

unsigned int i = ~0;
unsigned char c = ~0;

std::cout << "max uint = " << i << std::endl <<
             "max uchar = " << static_cast<short>(c) << std::endl;

Wręcz przeciwnie NIE zmienia wartości pierwotnej wartości i nie ma operatora przypisania złożonego, więc nie można na przykład wykonać a ~= 10 .

Nie należy mylić nieco logiki NOT ( ~ ) z logicznym NOT ( ! ); gdzie nieco mądre NIE odwróci każdego bitu, logiczne NIE użyje całej wartości, aby wykonać swoją operację, innymi słowy (!1) != (~1)

<< - lewy Shift

int a = 1;      // 0001b
int b = a << 1; // 0010b

std::cout << "a = " << a << ", b = " << b << std::endl;

Wynik

a = 1, b = 2

Dlaczego

Przesunięcie w lewo do bitu przesunie bity wartości lewej ręki ( a ) o liczbę określoną po prawej stronie ( 1 ), zasadniczo wypełniając najmniej znaczące bity zerami, przesuwając w ten sposób wartość 5 (binarnie 0000 0101 ) w lewo 4 razy (np. 5 << 4 ) da wartość 80 (dwójkowa 0101 0000 ). Możesz zauważyć, że przesunięcie wartości w lewo 1 raz jest również takie samo jak pomnożenie wartości przez 2, przykład:

int a = 7;
while (a < 200) {
    std::cout << "a = " << a << std::endl;
    a <<= 1;
}

a = 7;
while (a < 200) {
    std::cout << "a = " << a << std::endl;
    a *= 2;
}

Należy jednak zauważyć, że operacja przesunięcia w lewo przesunie wszystkie bity w lewo, w tym bit znaku, przykład:

int a = 2147483647; // 0111 1111 1111 1111 1111 1111 1111 1111
int b = a << 1;     // 1111 1111 1111 1111 1111 1111 1111 1110

std::cout << "a = " << a << ", b = " << b << std::endl;

Możliwe wyjście: a = 2147483647, b = -2

Chociaż niektóre kompilatory przynoszą wyniki, które wydają się oczekiwane, należy zauważyć, że jeśli opuścisz przesunięcie liczby ze znakiem, aby wpłynąć na bit znaku, wynik nie jest zdefiniowany . Nie jest również zdefiniowane, jeśli liczba bitów, o którą chcesz przesunąć, jest liczbą ujemną lub jest większa niż liczba bitów, jaką może pomieścić typ po lewej stronie, na przykład:

int a = 1;
int b = a << -1;  // undefined behavior
char c = a << 20; // undefined behavior

Przesunięcie bitowe w lewo nie zmienia wartości oryginalnych, chyba że jest przypisane konkretnie do użycia operatora złożonego przypisania bitowego <<= :

int a = 5;  // 0101b
a <<= 1;    // a = a << 1;

>> - przesunięcie w prawo

int a = 2;      // 0010b
int b = a >> 1; // 0001b

std::cout << "a = " << a << ", b = " << b << std::endl;

Wynik

a = 2, b = 1

Dlaczego

Przesunięcie mądrego bitu spowoduje przesunięcie bitów wartości lewej ręki ( a ) o liczbę określoną po prawej stronie ( 1 ); należy zauważyć, że chociaż operacja przesunięcia w prawo jest standardem, to, co dzieje się z bitami przesunięcia w prawo na podpisanej liczbie ujemnej, jest zdefiniowana implementacja, a zatem nie można zagwarantować, że będzie przenośna, na przykład:

int a = -2;    
int b = a >> 1; // the value of b will be depend on the compiler

Nie jest również zdefiniowane, jeśli liczba bitów, o które chcesz przesunąć, jest liczbą ujemną, na przykład:

int a = 1;
int b = a >> -1;  // undefined behavior

Przesunięcie bitowe w prawo nie zmienia wartości oryginalnych, chyba że przypisano je konkretnie do użycia operatora złożonego przypisania bitowego >>= :

int a = 2;  // 0010b
a >>= 1;    // a = a >> 1;


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow