Побитовые (поразрядные) операторы &, |, ^ и ~ напоминают логические операторы, но их область действия — биты, а не логические значения. Среди побитовых операторов есть также операторы сдвига, позволяющие переместить все биты числа влево или вправо на нужно количество разрядов.

Поразрядные (побитовые) операторы работают с 32-битными целыми числами в их двоичном представлении.
Двоичные числа представляют из себя строки, состоящие из нулей и единиц, в которых значение каждой единицы определяется ее позицией в данной строке.

Вот так, например, вы­глядият числа, записанные в формате 32-разрядного целого двоичного числа:

a = 0; // 00000000000000000000000000000000
a = 1; // 00000000000000000000000000000001
a = 2; // 00000000000000000000000000000010
a = 3; // 00000000000000000000000000000011
a = 4; // 00000000000000000000000000000100
a = 5; // 00000000000000000000000000000101

Каждый сдвиг влево на одну позицию означает удвоение значения, которое соответствует предыдущей позиции, находящейся справа.

Чтобы не путать, в какой системе счисления записано число, обычно в индексе пишут основание системы счисления, в которой оно записао. Например, число 5 в десятичной системе – 510,а в двоичной – 1012.

Чтобы перевести число в двоичную систему счисления необходимо делить его нацело на 2 пока не получим 0, затем необходимо записать все остатки от деления в обратном порядке.

Например, число 18, записываемое в двоичной системе счисления, имеет значение:

18 / 2 = 9 // остаток 0
9 / 2 = 4 // остаток 1
4 / 2 = 2 // остаток 0
2 / 2 = 1 // остаток 0
1 / 2 = 0 // остаток (обратите внимание!) 1
-------------------------------------------
            10010

Теперь записываем полученные остатки от деления в обратном порядке. Получаем, что число 18 в двоичном представлении будет выглядеть как 00000000000000000000000000010010 (обратите внимание, число состоит ровно из 32-битов), или, сокращенно, как 10010. Эти пять значимых битов и определяют фактическое значение числа 18.

Для преобразования из двоичной системы в десятичную используют формулу, состоящую из степеней основания 2:

a = a0 * 20 + a1 * 21 + a2 * 22 + … + an * 2n

где а – число в десятичной системе счисления; a0, a1, … an – цифры данного числа в двоичном представлении. Причём a0 — это последняя или правая цифра, а an – первая.

Например, возьмём двоичное число 1010012. Для перевода в десятичное запишем его как сумму по разрядам следующим образом:

1 * 25 + 0 * 24 + 1 * 23 + 0 * 22 + 0 * 21 + 1 * 20 = 41

Перепишем тоже самое, возведя в степени все основания 2:

1 * 32 + 0 * 16 + 1 * 8 + 0 * 4 + 0 * 2 + 1 * 1 = 41

Можно записать это в виде таблицы следующим образом:

512
(210)
256
(29)
128
(28)
64
(26)
32
(25)
16
(24)
8
(23)
4
(22)
2
(21)
1
(20)
1 0 1 0 0 1
+32 +0 +8 +0 +0 +1

Положительные числа хранятся в настоящем двоичном формате, в котором все биты, кроме знакового (крайний левый), представляют степени двойки: первый бит (справа) соответствует 20, второй – 21, третий – 22 и т. д. Если какие-либо биты не используются, они считаются равными нулю.

Теперь под каждой двоичной единицей напишите её эквивалент в нижней строчке таблицы и сложите получившиеся десятичные числа. Таким образом, двоичное число 1010012 равнозначно десятичному 4110.

Термин «целые числа со знаком» означает, что в этой форме могут быть представлены как положительные, так и отрицательные целые числа.

Отрицательные числа также хранятся в двоичном коде, но в формате, который на­зывается «дополнителъным кодом» (англ. two’s complement, иногда twos-complement).

Представление в «дополнительном коде» означает, что отрицательное значение числа (например 5 и -5) получается путем инвертирования числа (операция «побитовое НЕ», также известное как «обратный код» или «первое дополнение») и прибавлением к инверсии единицы («второе дополнение»).

К примеру, определим двоичное представление числа -5. Начнём с двоичного представления его абсолютного значения (5):

00000000000000000000000000000101

Инвертируем все разряды числа (заменим 0 на 1, а 1 на 0), получая таким образом обратный код (первое дополнение) числа 5:

11111111111111111111111111111010

Дополнительный код (второе дополнение) двоичного числа получается добавлением 1 (обычным двоичным сложением) к младшему значащему разряду его первого дополнения:

11111111111111111111111111111010 + 
                               1 = 
11111111111111111111111111111011

Итак, мы получили:

-5 = 11111111111111111111111111111011

В целых числах со знаком все биты, кроме 32-го, представляют само значение, тогда как 32-й бит определяет знак числа: если крайний-левый бит равен 0 – число положительное, если 1 – число отрицательное. Поэтому этот бит называется знаковым битом.

Однако в JavaScript такое двоичное представление чисел скрыто. Например, при выводе отрицательного числа в виде двоичной строки вы получите сокращенный двоичный код абсолютного значения этого числа со знаком «минус»:

  var num = -5;
  alert( num.toString(2) ); // "-101"

При изучении побитовых операторов вам могут пригодиться функции, которые облегчают перевод чисел из десятичного в двоичное представление и наоборот:

Выполнить код »

В следующей таблице перечислены все побитовые (поразрядные) операторы JavaScript:
Поразрядные операторы
Оператор Использование Описание
Побитовое И (AND) a & b Возвращает 1 в тех позициях результата, в кото­рых биты каждого из операндов равны 1.
Побитовое ИЛИ (OR) a | b Возвращает 1 в тех позициях результата, в кото­рых бит хотя бы одного из операндов равен 1.
Побитовое исключающее ИЛИ (XOR) a ^ b Возвращает 1 в тех позициях результата, в кото­рых бит только одного из операндов равен 1.
Побитовое НЕ (NOT) ~ a Заменяет каждый бит операнда на противопо­ложный.
Сдвиг влево a << b Сдвигает двоичное представление числа a на b разрядов влево заполняя освободившиеся справа разряды нулями.
Правый сдвиг, переносящий знак a >> b Сдвигает двоичное представление а на b разрядов вправо, отбрасывая уходящие биты.
Правый сдвиг с заполнением нулями a >>> b Сдвигает двоичное представление числа a на b разрядов вправо. Освобождающиеся разряды заполняются нулями.

Побитовые операторы, подобно логическим операторам, выполняют логические операции И, ИЛИ, НЕ, XOR, но с каждым отдельным битом целого числа. Cреди побитовых операторов есть также операторы сдвига <<, >>, >>> позволяющие переместить все биты числа влево или вправо на нужно количество разрядов. Побитовые операторы преобразуют свои операнды в 32-битные целые числа, представленные последовательностью битов. Дробная часть, если она есть, отбрасывается. Получившаяся в результате выполнения последовательность бит интерпретируется как обычное число.

Побитовое И (&) выполняет булеву операцию конъюнкции над каждой парой битов, которые стоят на одинаковых позициях в двоичных представлениях операндов. Другими словами, результат a & b равен 1, если оба соответствующих бита операндов равны 1; если же хотя бы один бит из пары равен 0, результирующий двоичный разряд равен 0.

Таблица истинности для этой операции выглядит так:

a b a & b
0 0 0
0 1 0
1 0 0
1 1 1

В следующем примере поразрядное И выполняется для чисел 38 и 3:

Выполнить код »

Как видите, только в одной позиции биты обоих операндов равны 1. Из-за этого все остальные биты результата обнуляются, что в итоге дает 000010. Как результат, получаем 0000102, или 210.

Побитовое ИЛИ (|) выполняет булеву операцию дизъюнкции над каждой парой битов, которые стоят на одинаковых позициях в двоичных представлениях операндов. Другими словами, результат a | b равен 0, если оба соответствующих бита операндов равны 0; если же хотя бы один бит из пары равен 1, результирующий двоичный разряд равен 1.

Таблица истинности для этой операции выглядит так:

a b a | b
0 0 0
0 1 1
1 0 1
1 1 1

В следующем примере поразрядное ИЛИ выполняется для чисел 38 и 3:

Выполнить код »

Каждый единичный бит любого из операндов переходит в результат. В итоге, получаем 1001112, или 3910.

Побитовое исключающее ИЛИ (^) выполняет исключающую дизъюнкцию над каждой парой битов, которые стоят на одинаковых позициях в двоичных представлениях операндов. Другими словами, результат a ^ b равен 0, если оба соответствующих бита операндов равны между собой; в противном случае, двоичный разряд результата равен 1.

Таблица истинности для этой операции выглядит так:

a b a ^ b
0 0 0
0 1 1
1 0 1
1 1 0

В следующем примере поразрядное исключающее ИЛИ выполняется для чисел 38 и 3:

Выполнить код »

Этот пример отличается от предыдущего только тем, что второй бит результата обнуляется, поскольку в обоих операндах он равен 1. Все остальные единичные биты переходят в результат, потому что у них нет пары. В итоге, получаем 1001012, или 3710.

Исключающее ИЛИ (^) с нулём в качестве одного из операндов можно использовать для округления математических выражений:

Выполнить код »

Учитывая, что у побитовых операторов достаточно низкий приоритет (ниже чем у арифметических операторов), скобки в приведенных выражениях могут быть опущены.

Побитовое отрицание НЕ (~) – это унарный оператор, который возвращает обратный код числа. Другими словами, на той позиции, где в двоичном представлении операнда был 0, в результате будет 1, и, наоборот, где была 1, там будет 0.

Таблица истинности для этой операции выглядит так:

a ~a
0 1
1 0

В следующем примере операция НЕ выполняется для числа 52:

Выполнить код »

По сути, число просто инвертируется, а затем новое значение уменьщается на 1.

Тот же ре­зультат можно получить следующим образом:

Выполнить код »

Учитывая тот факт, что битовые операции отбрасывают десятичную часть, их можно использовать для округления чисел.

Например, двойное НЕ (~) позволяет округлить числа до целых значений:

Выполнить код »

Оператор сдвига влево (<<) перемещает вле­во все биты первого операнда на расстояние, за­данное вторым операндом (целое число в диапазоне от 0 до 31). При сдвиге битов влево освободившиеся разряды справа заполняются нулями.

Например, если сдвинуть число 2 (10 в двоичном формате) на 5 битов влево, получится 64 (1000000 в двоичном формате):

Выполнить код »

В этом примере, при сдвиге числа влево на пять битов, справа пять пустых битов заполняются нулями, чтобы в результате получилось полное 32-разрядное число.

На заметку: Побитовый сдвиг влево на N позиций эквивалентен умножению числа на 2N. Таким образом, 4<<5 == 4*Math.pow(2,5).

Тот же ре­зультат, что и в предыдущем примере, можно получить следующим образом:

Выполнить код »

Хотя результаты одинаковы, поразрядные операторы выполняется гораздо быстрее, потому что они работает на самом низком уровне. Поэтому их использование, вместо математических функций, обеспечит неплохой прирост производительности.

Оператор >> (сдвиг битов вправо с сохранением знака) в точности противоположен сдвигу влево. Слева от оператора >> указывается операнд, биты которого будут сдвинуты, а справа – целое число в диапазоне от 0 до 31, которое определяет на сколько разрядов вправо будут сдвинуты биты операнда.

Если операнд положительный, то пустые места слева заполняются нулями. Если же изначально мы работаем с отрицательным числом, то все пустые места заполняются единицами. Это делается для сохранения знака, представленного крайним-левым битом. Поэтому он назван «переносящим знак».

В следующем примере выполняется сдвиг битов вправо на 2 разряда для чисел 9 и -9:

Выполнить код »

На заметку: Побитовый сдвиг вправо на N позиций эквивалентен делению этого числа на 2N. Побитовый сдвиг выполняется намного быстрее обычного деления.

Оператор >>> (сдвиг битов вправо с заполнением нулями) сдвигает все биты числа вправо на указанное количество разрядов. В отличие от сдвига вправо с со­хранением знака >>, теперь пустые слева биты заполняются нулями независимо от знака числа. Знаковый бит становится равным 0, поэтому результат всегда положителен.

В следующем примере выполняется сдвиг битов вправо на 2 разряда для чисел 9 и -9:

Выполнить код »

При сдвиге вправо с заполнением нулями отрицательное число в двоичном формате обрабатывается как положительное число. Учитывая, что отрицательное число является дополнением его абсолютного значения до двух, число становится очень большим, например, результатом -9 >>> 2 будет 1073741821 в десятичном формате.

  • Побитовые операторы работают с числами на самом низком уровне – уровне отдельных битов, поэтому в JavaScript они выполняются гораздо быстрее, чем другие операторы или методы.
  • Специальные значения NaN и Infinity в поразрядных операциях интерпретируются как О.
  • Если побитовые операторы применяются к нечисловому значению, то оно сначало автома­тически преобразуется в число с помощью функции Number() и только затем вы­полняется операция.
  • Побитовое НЕ ~ просто инвертирует все биты операнда, а затем новое значение уменьшает на 1.
  • С помощью двух побитовых НЕ ~~ можно округлить значение операнда или математического выражения.
  • Побитовое И & вернёт единицу только в том случае, если биты обоих операндов в этой позиции равны 1.
  • Побитовое ИЛИ | возвращает 1, если хотя бы один бит равен 1, и О, если оба бита равны О.
  • Побитовое исключающее ИЛИ ^ возвращает 1, только если один бит равен 1, и О, если оба бита равны 1.
  • Оператор сдвига влево << сдвигает все биты числа влево на указанное количество позиций. При этом пустые биты справа заполняются нулями.
  • Оператор сдвига вправо с сохранением знака >> сдвигает все биты числа вправо на указанное количество позиций. Если операнд положительный, то пустые места слева заполняются нулями. Если же изначально мы работаем с отрицательным числом, то все пустые места заполняются единицами.
  • Сдвиг вправо с заполнением нулями >>> сдвигает все биты числа вправо на указанное количество позиций. В отличие от сдвига вправо с со­хранением знака >>, теперь пустые биты заполняются нулями независимо от знакового бита.

  • Какое из двух выражений истинно?

    а >> b + 7 == а >> (b + 7);
    а >> b + 7 == (а >> b) + 7;
    
    Показать решение

    Решение:

    Поскольку у оператора сложения более высокий приоритет, чем у оператора сдвига, правильный ответ: а >> b + 7 == а >> (b + 7). Так что если вам требуется выполнить операцию (а>>b) + 7, то без скобок не обойтись.

    Выполнить код »
  • Решите примеры: y = 4 * 25 и z = 64 / 25 используя побитовые операторы.

    y = 4 * 25;
    z = 64 / 25;
    
    Показать решение

    Решение:

    Известно, что сдвиг влево на n разрядов эквивалентен умножению на 2n, а сдвиг вправо — делению на 2n. Ответ: y = 4 << 5; z = 64 >> 5;

    Выполнить код »
  • При каких значениях переменной х сработает условие: if (x & 1)? Обоснуйте свой ответ.

    var x;
    if (x & 1) {
      alert( 'сработало!' );
    }
    
    Показать решение

    Решение:

    Чтобы условие if сработало, результатом выражения (x & 1) должна быть 1 (правда). Оператор & вернёт 1, только если со­ответствующие биты в обоих операндах также рав­ны 1. Если хотя бы один из них равен 0, то и бит результата равен 0. Цифра 1 в двоичном представлении состоит из нулей и только младший бит справа равен 1. У нечетных чисел младший бит тоже равен единице. Таким образом оператор & вернёт 1 (true), если в качестве переменной х – нечетное число.

    Выполнить код »
  • Что выведет каждый alert, если переменные а и b принимают булевы значения?

      var а = true; 
      var b = false;
    
      alert( a|b );
      alert( a&b );
      alert( a^b );
      alert( !a&b|a&!b );
    
    Показать решение

    Решение:

    Значения переменных а и b будут преобразованы соответственно в числа 1 и 0. Двоичные форматы этих чисел очень просты. Пользуясь таблицами истинности для каждого побитного оператора, нетрудно понять, что в результате мы получим числа 1 или 0, а не true или false.

    Выполнить код »

Комментарии

пожелания к комментариям…
  • Приветствуются комментарии, соответствующие теме урока: вопросы, ответы, предложения.
  • Одну строчку кода оборачивайте в тег <code>, несколько строчек кода — в теги <pre><code>...ваш код...</code></pre>.
  • Допускаются ссылки на онлайн-песочницы (codepen, plnkr, JSBin и др.).