Operatory bitowe

Operatory bitowe traktuję swoje operandy jako sekwencje 32 bitów (zer i jedynek), bardziej niż jako dziesiętne, szesnastkowe czy ósemkowe wartości liczbowe. Przykładowo, reprezentacją binarną dziesiętnej liczby 9 jest 1001. Operatory bitowe dokonują operacji na takich właśnie reprezentacjach bitowych, zwracają jednak standardowe JavaScriptowe wartości liczbowe.

Poniższa tabela zawiera podsumowanie operatorów bitowych w języku JavaScript:

Operator Użycie Opis
Bitowe AND a & b Zwraca 1 na każdej pozycji bitowej, dla której odpowiadające jej bity obydwu operandów mają wartość 1.
Bitowe OR a | b Zwraca 1 na każdej pozycji bitowej, dla której jeden lub oba odpowiadające jej bity operandów mają wartość 1.
Bitowe XOR a ^ b Zwraca 1 na każdej pozycji bitowej, dla której dokładnie jeden bit spośród odpowiadających jej bitów operandów ma wartość jeden.
Bitowe NOT ~ a Neguje bity swojego operandu.
Przesunięcie w lewo a << b Przesuwa a w binarnej reprezentacji o b bitów w lewo (gdzie b < 32), dodając zera z prawej strony.
Przesunięcie w prawo z propagacją znaku a >> b Przesuwa a w binarnej reprezentacji o b bitów w prawo (gdzie b < 32), odrzucając b bitów z prawej strony.
Przesunięcie w prawo z dopełnieniem zerami a >>> b   Przesuwa a w binarnej reprezentacji o b bitów w prawo (gdzie b < 32), odrzucając b bitów z prawej strony i uzupełniając sekwencję zerami z lewej strony.

32-bitowe wartości całkowite ze znakiem

Operandy wszystkich operatorów bitowych są konwertowane do 32-bitowych wartości całkowitych w dwójkowym kodzie uzupełnieniowym, z wyjątkiem przesunięcia w prawo z dopełnieniem zerami, które zwraca 32-bitową wartość całkowitą bez znaku. Dwójkowy kod uzupełnieniowy oznacza, że liczba przeciwna danej wartości (na przykład 5 i -5) ma wszystkie bity zanegowane w stosunku do tejże wartości (bitowe NOT liczby, znane również jako jedynkowe dopełnienie liczby) plus jeden. Przykładowo, dziesiętna liczba 314 ma następującą postać dwójkową:

00000000000000000000000100111010

Reprezentacja binarna ~314, czyli jedynkowe dopełnienie 314:

11111111111111111111111011000101

-314 ma ostatecznie następującą postać, będącą dwójkowym dopełnieniem 314:

11111111111111111111111011000110

Dopełnienie dwójkowe gwarantuje, że skrajny lewy bit będzie zerem dla liczby dodatniej i jedynką dla liczby ujemnej – bit ten zwany jest stąd bitem znaku.

Liczba 0 jest wartością całkowitą, złożoną w całości z bitów o wartości 0.

0 (base 10) = 00000000000000000000000000000000 (base 2)

Liczba -1 jest wartością całkowitą, złożoną z samych bitów o wartości 1.

-1 (base 10) = 11111111111111111111111111111111 (base 2)

Liczba -2147483648 (reprezentacja szesnastkowa: -0x80000000) jest wartością całkowitą, złożoną z samych bitów o wartości 0, z wyjątkiem pierwszego (znajdującego się najbardziej z lewej strony) bitu.

-2147483648 (base 10) = 10000000000000000000000000000000 (base 2)

Liczba 2147483647 (rprezentacja szesnastkowa: 0x7fffffff) jest wartością całkowitą, złożoną jedynie z bitów o wartości 1, z wyjątkiem pierwszego (skrajnie lewego) bitu.

2147483647 (base 10) = 01111111111111111111111111111111 (base 2)

Liczby -2147483648 i 2147483647 stanowią odpowiednio minimalną i maksymalną wartość całkowitą, którą można zapisać przy użyciu 32-bitowej liczby ze znakiem.

Bitowe operatory logiczne

Idea działania bitowych operatorów logicznych jest następująca:

  • Operandy są konwertowane do 32-bitowych wartości całkowitych, wyrażanych jako sekwencja bitów (zer i jedynek). Dla liczb o więcej niż 32 bitach odrzuca się najbardziej znaczące bity. Przykładowo, następująca wartość całkowita zajmująca więcej niż 32 bity będzie przekonwertowana do 32-bitowej wartości w następujący sposób:
    Przed: 11100110111110100000000000000110000000000001
    Po:                10100000000000000110000000000001
  • Każdy z bitów pierwszego operandu parowany jest z odpowiadającym mu bitem drugiego operandu: pierwszy z pierwszym, drugi z drugim i tak dalej (idąc od prawej strony).
  • Operator jest stosowany na każdej parze bitów, a wynik jest tworzony bitowo.

& (Bitowe AND)

Stosuje operację AND (koniunkcję) na każdej parze bitów. a AND b daje 1 wtedy i tylko wtedy, gdy zarówno a, jak i b będą miały wartość 1. Tablica prawdy dla operacji AND przedstawiona jest poniżej:

a b a AND b
0 0 0
0 1 0
1 0 0
1 1 1
.    9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)

Bitowa koniunkcja (AND) dowolnej wartości x i 0 zawsze daje 0.

| (Bitowe OR)

Stosuje operację OR (alternatywę) na każdej parze bitów. a OR b daje 1 wtedy i tylko wtedy, gdy a lub b ma wartość 1. Tablica prawdy dla operacji OR przedstawina jest poniżej:

a b a OR b
0 0 0
0 1 1
1 0 1
1 1 1
.    9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)

Zastosowanie alternatywy bitowej (OR) dowlonej wartości x i 0 zawsze daje x.

^ (Bitowe XOR)

Stosuje bitowe XOR (alternatywę wykluczającą) na każdej parze bitów. a XOR b daje 1 wtedy i tylko wtedy, gdy a i b mają różne wartości. Tablica prawdy dla operacji XOR przedstawiona jest poniżej:

a b a XOR b
0 0 0
0 1 1
1 0 1
1 1 0
.    9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)

Zastosowanie bitowej alternatywy wykluczającej (XOR) dowolnej wartości x i 0 daje x.

~ (Bitowe NOT)

Stosuje operator NOT (negację) na każdym bicie. NOT a zwraca odwróconą wartość (inaczej zwaną dopełnieniem jedynkowym) a. Tablica prawdy operacji NOT przedstawiona jest poniżej:

a NOT a
0 1
1 0
 9 (base 10) = 00000000000000000000000000001001 (base 2)
               --------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

Bitowa negacja (NOT) dowolnej wartości x daje -(x + 1). Przykładowo, ~-5 daje 4.

Zauważmy, że z powodu używania 32-bitowej reprezentacji liczb, zarówno ~-1, jak i ~4294967295 (232-1) daje wynik 0.

Bitowe operatory przesunięcia

Bitowe operatory przesunięcia przyjmują dwa operandy: pierwszy jest wartością do przesunięcia, a drugi wskazuje liczbę pozycji bitowych, o którą pierszy operand ma być przesunięty. Kierunek operacji przesunięcia jest zdefiniowany przez użycie danego operatora.

Operatory przesunięcia konwertują swoje operandy do 32-bitowych wartości całkowitych w porządku big-endian (znanym też pod nazwą grubokońcowość) i zwraca wynik tego samego typu, co lewy operand. Użytych będzie przy tym jedynie pięć najniższych bitów prawego operandu.

<< (Przesunięcie w lewo)

Operator ten przesuwa pierwszy operand o określoną liczbę bitów w lewo. Nadmiarowe bity przesunięte poza zakres z lewej strony są odrzucane. Z prawej strony sekwencja uzupełniana jest zerami.

Przykładowo, 9 << 2 daje 36:

.    9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)

Bitowe przesuwanie dowolnej wartości x w lewo o y bitów daje x * 2 ** y.
Tak więc, przykładowo: 9 << 3 można przetłumaczyć jako: 9 * (2 ** 3) = 9 * (8) = 72.

>> (Przesunięcie w prawo z propagacją znaku)

Operator ten przesuwa pierwszy operand o określoną liczbę bitów w prawo. Nadmiarowe bity przesunięte z prawej strony poza zakres są odrzucane. Sekwencja jest uzupełniana z lewej strony wartościami skrajnie lewego bitu. Kiedy skrajnie lewy bit ma taką samą wartość, jak poprzedni skrajnie lewy bit, znak się nie zmienia – stąd nazwa „z propagacją znaku”.

Przykładowo, 9 >> 2 daje 2:

.    9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

Podobnie, -9 >> 2 daje -3, ponieważ zachowywany jest znak:

.    -9 (base 10): 11111111111111111111111111110111 (base 2)
                   --------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

>>> (Przesunięcie w prawo z dopełnieniem zerami)

Operator ten przesuwa pierwszy operand o określoną liczbę bitów w prawo. Nadmiarowe bity przesunięte poza zakres z prawej strony są odrzucane. Sekwencja jest uzupełniana z lewej strony zerami. Bit znaku staje się zerem, dlatego też wynik jest zawsze nieujemny. W przeciwieństwie do pozostałych operatorów bitowych, przesunięcie w prawo z dopełnieniem zerami zwraca 32-bitową wartość całkowitą bez znaku.

Dla liczb nieujemnych, przesunięcie w prawo z zerami i z zachowaniem znaku dają taki sam wynik. Przykładowo, 9 >>> 2 daje 2, tak samo jak 9 >> 2:

.     9 (base 10): 00000000000000000000000000001001 (base 2)
                   --------------------------------
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

Inaczej wygląda to jednak w przypadku liczb ujemnych. Przykładowo, -9 >>> 2 daje 1073741821, co jest różne od -9 >> 2 (które daje -3):

.     -9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

Przykłady

Flagi i maski bitowe

Bitowe operatory logiczne są często używane do tworzenia, manipulowania i odczytywania sekwencji flag, które działają jak zmienne binarne. Zmienne mogą być używane zamiast tych sekwencji, ale flagi zajmują znacznie mniej pamięci (32-krotnie).

Załóżmy, że mamy następujące 4 flagi:

  • flaga A: mamy problem z mrówkami,
  • flaga B: mamy nietoperza,
  • flaga C: mamy kota,
  • flaga D: mamy kaczkę.

Flagi te są reprezentowane przez sekwencję bitów: DCBA. Kiedy flaga jest ustawiona, odpowiedni bit ma wartość 1. Kiedy flaga jest wyczyszczona, właściwy bit ma wartość 0. Załóżmy, że zmienna flagi ma binarną wartość 0101:

var flagi = 5;   // binarnie 0101

Wartość ta wskazuje, że:

  • flaga A ma wartość „prawda” (mamy problem z mrówkami);
  • flaga B ma wartość „fałsz” (nie mamy nietoperza);
  • flaga C ma wartość „prawda” (mamy kota);
  • flaga D ma wartość „fałsz” (nie mamy kaczki);

Ponieważ operatory bitowe są 32-bitowe, 0101 to faktycznie 00000000000000000000000000000101, ale zera wiodące mogą być pominięte, gdyż nie zawierają żadnej znaczącej informacji.

Maska bitowa jest sekwencją bitów pozwalającą na manipulowanie flagami lub odczytywanie ich wartości. Zazwyczaj „podstawowe” maski bitowe dla każdej flagi będą zdefiniowane w następujący sposób:

var FLAGA_A = 1; // 0001
var FLAGA_B = 2; // 0010
var FLAGA_C = 4; // 0100
var FLAGA_D = 8; // 1000

Nowe maski bitowe mogą być stworzone przy użyciu operatorów bitowych na tychże podstawowych maskach. Przykładowo, maska 1011 może być stworzona przy użyciu operatora OR na zmiennych FLAGA_A, FLAGA_B i FLAGA_D.

var maska = FLAGA_A | FLAGA_B | FLAGA_D; // 0001 | 0010 | 1000 => 1011

Pojedyncze wartości flag mogą być wyekstrahowane przez użycie operatora AND na fladze i właściwej masce – bit z wartością 1 „ekstrahuje” odpowiednią flagę. Maska bitowa maskuje wszystkie nieistotne flagi przez koniunkcję ich bitów z zerami maski (stąd nazwa „maska”). Przykładowo, maska 0100 może być użyta do sprawdzenia, czy flaga C jest ustawiona:

// czy mamy kota
if (flagi & FLAGA_C) { // 0101 & 0100 => 0100 => true
   // coś zrób
}

Maska z ustawionymi wieloma flagami działa jak alternatywa logiczna. Przykładowo, poniższe dwie wersje są równoważne:

// czy mamy nietoperza lub czy mamy kota
// (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
if ((flagi & FLAGA_B) || (flagi & FLAGA_C)) {
   // coś zrób
}
// czy mamy nietoperza lub kota
var maska = FLAGA_B | FLAGA_C; // 0010 | 0100 => 0110
if (flagi & maska) { // 0101 & 0110 => 0100 => true
   // coś zrób
}

Flagi mogą być ustawione przez użycie na nich i masce operacji OR, gdzie każdy z bitów z wartością 1 będzie ustawiał odpowiednią flagę, jeśli nie jest już ustawiona. Przykładowo, maska 1100 może być użyta do ustawienia flag C i D:

// tak, możemy mieć kota i kaczkę
var maska = FLAGA_C | FLAGA_D; // 0100 | 1000 => 1100
flagi |= maska;   // 0101 | 1100 => 1101

Flagi mogą być czyszczone przez użycie operatora AND z maską, gdzie każdy z bitów z wartością 0 będzie czyścił odpowiednią flagę, jeśli nie jest już wyczyszczona. Maska może być stworzona przez użycie operatora NOT na maskach podstawowych. Przykładowo, maska 1010 może być użyta do wyczyszczenia flag A i C:

// nieprawdą jest, że mamy problem z mrówkami lub posiadamy kota
var maska = ~(FLAG_A | FLAG_C); // ~0101 => 1010
flagi &= maska;   // 1101 & 1010 => 1000

Maska może być również stworzona przez wyrażenie ~FLAG_A & ~FLAG_C (z praw De Morgana):

// nie, nie mamy problemu z mrówkami i nie posiadamy kota
var maska = ~FLAGA_A & ~FLAGA_C;
flagi &= maska;   // 1101 & 1010 => 1000

Flagi mogą być przełączane przez użycie operatora XOR z maską bitową, gdzie każðy bit będzie przełączał odpowiednią flagę. Przykładowo, maska 0110 może być użyta do przełączenia flag B i C:

// jeśli nie mieliśmy nietoperza, teraz go mamy,
// a jeśli go mieliśmy – pa, pa, nietoperku!
// tak samo z kotami
var maska = FLAGA_B | FLAGA_C;
flagi = flagi ^ maska;   // 1100 ^ 0110 => 1010

Flagi mogą być odwracane przez operator NOT:

// przechodzimy do równoległego wszechświata...
flagi = ~flagi;    // ~1010 => 0101

Conversion snippets

Konwersja binarnej zmiennej typu String do liczby dziesiętnej typu Number:

var sBinString = '1011';
var nMojaLiczba = parseInt(sBinString, 2);
alert(nMojaLiczba); // wypisuje 11, tzn. binarnie 1011

Konwersja dziesiętnej liczby do binarnego Stringa:

var nMojaLiczba = 11;
var sBinString = nMojaLiczba.toString(2);
alert(sBinString); // wypisuje 1011, tzn. dziesiętnie 11

Automatyczne tworzenie masek

Możesz stworzyć wiele masek ze zbioru wartości typu Boolean values, na przykład:

function createMask() {
  var nMask = 0, nFlag = 0, nLen = arguments.length > 32 ? 32 : arguments.length;
  for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);
  return nMask;
}
var mask1 = createMask(true, true, false, true); // 11, i.e.: 1011
var mask2 = createMask(false, false, true); // 4, i.e.: 0100
var mask3 = createMask(true); // 1, i.e.: 0001
// itd.

alert(mask1); // wypisuje 11, czyli binarnie: 1011

Algorytm odwrotny: tablica zmiennych boolowskich z maski

Jeśli chcesz stworzyć tablicę złożoną ze zmiennych boolowskich, możesz użyć następującego kodu:

function arrayFromMask(nMask) {
  // nMask musi być pomiędzy -2147483648 a 2147483647
  if (nMask > 0x7fffffff || nMask < -0x80000000) { 
    throw new TypeError('arrayFromMask - out of range'); 
  }
  for (var nShifted = nMask, aFromMask = []; nShifted; 
       aFromMask.push(Boolean(nShifted & 1)), nShifted >>>= 1);
  return aFromMask;
}

var array1 = arrayFromMask(11);
var array2 = arrayFromMask(4);
var array3 = arrayFromMask(1);

alert('[' + array1.join(', ') + ']');
// wypisuje "[true, true, false, true]", tzn.: 11, tzn.: 1011

Możesz przetestować obydwa algorytmy naraz:

var nTest = 19; // nasza maska
var nResult = createMask.apply(this, arrayFromMask(nTest));

alert(nResult); // 19

Jedynie dla celów dydaktycznych (jako że istnieje metoda Number.toString(2)), pokażemy jak można zmodyfikować algorytm arrayFromMask tak, by tworzył zmienną String zawierającą binarną reprezentację danej liczby, zamiast tablicy zmiennych typu Boolean:

function createBinaryString(nMask) {
  // nMask musi być pomiędzy -2147483648 a 2147483647
  for (var nFlag = 0, nShifted = nMask, sMask = ''; nFlag < 32;
       nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
  return sMask;
}

var string1 = createBinaryString(11);
var string2 = createBinaryString(4);
var string3 = createBinaryString(1);

alert(string1);
// wypisuje 00000000000000000000000000001011, i.e. 11

Specyfikacje

Specyfikacja Status Komentarz
ECMAScript 1st Edition (ECMA-262) Standard Definicja początkowa.
ECMAScript 5.1 (ECMA-262) Standard Zdefiniowane w kilku sekcjach specyfikacji: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators
ECMAScript 2015 (6th Edition, ECMA-262) Standard Zdefiniowane w kilku sekcjach specyfikacji: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators
ECMAScript (ECMA-262) Living Standard Zdefiniowane w kilku sekcjach specyfikacji: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators

Wsparcie przeglądarek

No compatibility data found. Please contribute data for "javascript.operators.bitwise" (depth: 1) to the MDN compatibility data repository.

Zobacz też