Побитовые (поразрядные) операторы &
, |
, ^
и ~
напоминают логические операторы, но их область действия — биты, а не логические значения. Среди побитовых операторов есть также операторы сдвига, позволяющие переместить все биты числа влево или вправо на нужно количество разрядов.
Формат 32-битного целого числа со знаком
Поразрядные (побитовые) операторы работают с 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>
.