JavaScript, как и многие другие языки программирования, поддерживает побитовые операторы. Эти операторы работают на битовом уровне и полезны, если мы хотим манипулировать отдельными битами. В JavaScript числа всегда хранятся как числа с плавающей запятой двойной точности, что соответствует стандарту IEEE 754. Однако эти операторы обрабатывают их как 32-битные двоичные числа со знаком и работают с ними.

Поразрядное И (&)

Оператор & - это бинарный оператор, который выполняет побитовое И для каждой пары соответствующих битов. Оператор & возвращает 1 тогда и только тогда, когда оба бита равны 1, в противном случае он возвращает 0.

Вот все возможные значения операции И:

0&0 = 0
0&1 = 0
1&0 = 0
1&1 = 1

Например, 5 и 6 дают 4

5  00000101 
6  00000110
&------------
4  00000100

Поразрядное ИЛИ (|)

| Оператор - это бинарный оператор, который выполняет поразрядное ИЛИ над каждым из соответствующих битов. | Оператор возвращает 1, если какой-либо из битов равен 1, и возвращает 0, только если оба бита равны 0.

Вот все возможные значения операции ИЛИ.

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

Например, 5 | 6 результатов в 7

5  00000101 
6  00000110
|------------
7  00000111

Побитовое исключающее ИЛИ (^)

Побитовое исключающее ИЛИ (исключающее ИЛИ) - это бинарный оператор, выполняющий исключающее ИЛИ для каждой пары соответствующих битов. Если оба бита одинаковы, он возвращает 0, а если биты разные (т. Е. Один равен 1, а другой - 0), он возвращает 1.

Вот все возможные значения операции XOR:

0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0

Например, 5 ^ 6 дает 3

5  00000101 
6  00000110
^------------
3  00000011

Побитовое НЕ (~)

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

Вот все возможные значения операции побитового НЕ (~):

~0 == 1
~1 == 0 

Например, ~ 5 дает -6

5  ==>   00000000000000000000000000000101
~5 ==> ~(00000000000000000000000000000101)
         11111111111111111111111111111010 ==> -6

Мы можем проверить наш ответ, используя следующее свойство:

~a = -(a+1)  
~5 = -(5+1) = -6

Левый Shift (‹<)

Оператор сдвига влево принимает 2 значения. Первый - это операнд, который нам нужно сдвинуть, а второй - количество битов ( операнд ‹* количество бит ). Этот оператор сдвигает операнд влево на количество бит. Количество нулевых (0) битов, сдвинутых справа, зависит от количества битов, которые мы сдвигаем влево. Лишние биты, сдвинутые влево, отбрасываются.

Например, 10 ‹---------------- 2 дает 40

     10 ==> 00000000000000000000000000001010
10 << 2 ==> 00000000000000000000000000101000 = 40 ( decimal )

Побитовое смещение любого числа a влево на m бит приводит к * (2 ** m)
, где ** - это возведение в степень.

10<<2 can be calculated as 10*(2**2) = 10*(4) = 40 

Распространение знака, сдвиг вправо (››)

Оператор сдвига вправо принимает 2 значения. Первый - это операнд, который нам нужно сдвинуть, а второй - количество битов ( операнд ›› количество битов ). Копирует знаковый бит (крайний левый бит). Если бит знака равен 1, мы заполняем его единицами, а если бит знака равен 0, мы заполняем его нулями. Знак целого числа всегда сохраняется, а лишние биты, которые были смещены вправо, отбрасываются.

Например, 32 ›› 3 дает 4, а -32 ›› 3 дает -4

32      ==> 00000000000000000000000000100000
32 >>3 ==> 00000000000000000000000000000100 --> 4(base 10)
We shift it to the right by 3 places and add in (000)to the left side . We discarded 000 on the right.
Note :We added 000 as 0 was the sign bit.
-32      ==> 11111111111111111111111111100000 (in 2’s complement)
-32 >>3 ==>  11111111111111111111111111111100 --> -4(base 10)
We shift it to the right by 3 places and add in (111) to the left side. We discarded 000 on the right.
Note: we added 111 to the right as 1 is the sign bit 

Сдвиг вправо с нулевым заполнением (›››)

Побитовый сдвиг вправо без знака очень похож на обычный побитовый сдвиг вправо, за исключением того, что все пустые биты слева заполнены нулями. Лишние биты, которые были смещены вправо, отбрасываются. Это приводит к тому, что наш знаковый бит равен 0, и, таким образом, мы получаем целое число без знака. Для положительных целых чисел и ››, и ››› дадут нам одинаковое значение.

Например, 32 ››› 3 результатов в 4 и -32 ››› 3 результатов в 536870908

32      ==> 00000000000000000000000000100000 
32 >>>3 ==> 00000000000000000000000000000100 --> 4(base 10) 
We shift it to the right by 3 places and add in (000) to the left side . We discarded 000 on the right.
-32      ==> 11111111111111111111111111100000 (in 2’s complement)
-32 >>>3 ==> 00011111111111111111111111111100 --> 536870908(base 10)
We shift it to the right by 3 places and add in (000) to the left side. We discarded 000 on the right.

Приложения побитовых операторов

  1. Применение побитового И (&)

Проверьте, четное или нечетное число

Мы можем проверить, является ли число четным или нечетным, проверив его младший бит. Чтобы понять это, давайте взглянем на четные и нечетные числа:

odd numbers 
5 is 0101 
3 is 0011 
==> the least significant bit for both of them is 1
even numbers 
2 is 0010 
4 is 0100 
==> the least significant bit for both of them is 0

Мы увидим, что наименее значимые биты для нечетных чисел равны 1, а для четных - 0. Для проверки этого можно использовать оператор &, поскольку он приведет к 1 тогда и только тогда, когда оба бита равны 1. Если мы возьмем вход и AND это с 1, мы сможем проверить младший бит, который приведет к 1, если он нечетный, или 0, если он четный:

Even Number
60 ->  00111100 [input]
&1 ->  00000001  
---------------------------------------
 0 ->  00000000 [output]
Odd Number
53 ->  00110101 [input]
&1 ->  00000001  
---------------------------------------
 0 ->  00000001 [output]

2. Применение побитового ИЛИ (|)

Преобразование символа ASCII из верхнего регистра в нижний

Мы можем преобразовать значения ascii «A-Z» в «a-z» с помощью оператора побитового ИЛИ (|). Чтобы понять, мы должны помнить, что '|' может использоваться для установки определенных битов в 1. Если мы посмотрим на значения ниже, вы заметите, что все двоичные значения одинаковы, за исключением 6-го бита (если мы считаем справа и рассматриваем младший бит как 1-й бит).

Letter    Binary       Letter    Binary
  a      01100001        A      01000001
  b      01100010        B      01000010
  c      01100011        C      01000011
  d      01100100        D      01000100
  e      01100101        E      01000101

Шестой бит для верхнего регистра равен 0, а шестой бит для строчных значений равен 1. Все, что нам нужно сделать сейчас, это использовать '|', чтобы установить 6-й бит в 1, что можно сделать, выполнив побитовое ИЛИ с 00100000 (32 в десятичной системе). .

Например: давайте преобразовать "A" в "a".

A 01000001 [input]
  00100000 [mask ]
-------------------
a 01100001 [output]

3. Применение XOR (^)

Заменять номера местами без дополнительных переменных

Важно отметить, что подобный обмен местами работает только для целых чисел, он не работает для строк или даже чисел с плавающей запятой.

Мы можем использовать XOR для обмена на месте. Например, возьмем a = 10 и b = 12. Мы хотим, чтобы результат был a = 12 и b = 10, чего можно добиться с помощью оператора ^.

Для начала важно знать следующие свойства:

x ^ 0 = x
x ^ x = 0
x ^ y = y ^ x  ( xor is commutative)
x ^ (y ^ z) = (x ^ y)^z ( xor is associative )
  1. a = a ^ b
  2. б = а ^ б. Из шага 1 мы знаем, что a - это a ^ b, и затем можем подставить его в b = a ^ b. Таким образом, b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a. Теперь b содержит значение a.
  3. а = а ^ б. Из шага 1 мы знаем, что a - это a ^ b, а из шага 2 мы знаем, что b содержит значение a. Таким образом, a = (a ^ b) ^ a = b ^ (a ^ a) = b ^ 0 = b. Наконец, a будет содержать значение b.

4. Применение побитового НЕ НЕ (~)

Проверка наличия / отсутствия подстроки в другой строке

Использование ‘~’ перед выражением indexOf () приводит к истинному / ложному значению вместо числового индекса. Мы можем сделать это, когда нам не нужно значение индекса и мы просто хотим проверить, присутствует ли что-то или нет.

~someString.indexOf(something)

Если возвращенный индекс равен -1, мы выполняем ~ (-1), что приведет к 0, что является ложным значением. Все другие значения, кроме -1, будут давать истинные значения.

Примечание: вместо if (~ x.indexOf (substring)) мы могли бы сделать if (x.indexOf (substring) ›= 0), поскольку оба способа верны. Однако я пытался использовать только побитовые операторы.