Что такое побитовые операторы?

130

Я тот, кто пишет код просто для развлечения и не особо углублялся в него ни в академической, ни в профессиональной среде, поэтому такие вещи, как эти побитовые операторы, действительно ускользают от меня.

Я читал статью о JavaScript, который явно поддерживает побитовые операции. Я все время вижу, что эта операция упоминается местами, и я пытался прочитать, чтобы выяснить, что именно, но я просто, похоже, не понимаю ее вообще. Так что они? Было бы здорово использовать наглядные примеры! : D

Еще несколько вопросов - каковы практические применения побитовых операций? Когда вы можете их использовать?

щелчок
источник
2
Если у вас возникнут дополнительные вопросы, вы можете добавить новый вопрос SO и сослаться на него. Так вы, вероятно, получите лучший набор ответов.
Грег Хьюгилл

Ответы:

187

Поскольку никто не затронул тему, почему они полезны:

Я много использую побитовые операции при работе с флагами. Например, если вы хотите передать ряд флагов операции (скажем, File.Open()с включенными режимами чтения и записи), вы можете передать их как одно значение. Это достигается путем присвоения каждому возможному флагу собственного бита в битовом наборе (byte, short, int или long). Например:

 Read: 00000001
Write: 00000010

Итак, если вы хотите передать чтение И запись, вы должны передать (READ | WRITE), который затем объединяет два в

00000011

Что затем можно расшифровать на другом конце, например:

if ((flag & Read) != 0) { //...

который проверяет

00000011 &
00000001

который возвращается

00000001

который не равен 0, поэтому флаг указывает READ.

Вы можете использовать XOR для переключения различных битов. Я использовал это при использовании флага для указания направленных входов (вверх, вниз, влево, вправо). Например, если спрайт движется горизонтально, и я хочу, чтобы он повернулся:

     Up: 00000001
   Down: 00000010
   Left: 00000100
  Right: 00001000
Current: 00000100

Я просто выполняю XOR текущего значения с помощью (LEFT | RIGHT), который в этом случае отключает LEFT и RIGHT.

Битовый сдвиг полезен в нескольких случаях.

x << y

такой же как

х * 2 у

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

int val = (A << 24) | (B << 16) | (C << 8) | D;

Предполагая, что A - самый старший байт, а D - младший. В итоге получилось бы:

A = 01000000
B = 00000101
C = 00101011
D = 11100011
val = 01000000 00000101 00101011 11100011

Цвета часто хранятся таким образом (причем старший байт либо игнорируется, либо используется как альфа):

A = 255 = 11111111
R = 21 = 00010101
G = 255 = 11111111
B = 0 = 00000000
Color = 11111111 00010101 11111111 00000000

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

Int Alpha = Color >> 24
Int Red = Color >> 16 & 0xFF
Int Green = Color >> 8 & 0xFF
Int Blue = Color & 0xFF

0xFFтакое же, как 11111111. По сути, для Red вы бы сделали это:

Color >> 16 = (filled in 00000000 00000000)11111111 00010101  (removed 11111111 00000000)
00000000 00000000 11111111 00010101 &
00000000 00000000 00000000 11111111 =
00000000 00000000 00000000 00010101 (The original value)
Эд Марти
источник
x << n, значит, n должно быть в виде значения 2 ^?
Ahmed C,
28

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

int x = 5 & 6;

Ответ заключается в двоичном разложении каждого входа:

  5 = 0 0 0 0 0 1 0 1
& 6 = 0 0 0 0 0 1 1 0
---------------------
      0 0 0 0 0 1 0 0

Каждая пара битов в каждом столбце обрабатывается функцией «И», чтобы получить соответствующий выходной бит в нижней строке. Итак, ответ на приведенное выше выражение - 4. ЦП выполнил (в этом примере) 8 отдельных операций «И» параллельно, по одной для каждого столбца.

Я упоминаю об этом, потому что до сих пор помню это «АГА!». момент, когда я узнал об этом много лет назад.

Грег Хьюгилл
источник
Вау, теперь это имеет гораздо больший смысл. Это звучало намного сложнее, чем кажется. Спасибо. Я не уверен, какой ответ выбрать в качестве правильного, так как есть множество хороших ответов, и я не могу проголосовать, поэтому ... спасибо
нажмите
27

Побитовые операторы - это операторы, которые работают понемногу.

И равно 1, только если оба его входа равны 1.

ИЛИ равно 1, если один или несколько его входов равны 1.

XOR равен 1, только если ровно один из его входов равен 1.

НЕ равно 1, только если его вход равен 0.

Их лучше всего описать как таблицы истинности. Возможности входов находятся вверху и слева, результирующий бит - одно из четырех (два в случае НЕ, поскольку он имеет только один вход) значений, показанных на пересечении двух входов.

AND|0 1      OR|0 1
---+----    ---+----
  0|0 0       0|0 1
  1|0 1       1|1 1

XOR|0 1     NOT|0 1
---+----    ---+---
  0|0 1        |1 0
  1|1 0

Одним из примеров является то, что если вам нужны только младшие 4 бита целого числа, вы И его с 15 (двоичный 1111), поэтому:

    203: 1100 1011
AND  15: 0000 1111
------------------
 IS  11: 0000 1011
paxdiablo
источник
16

Все эти побитовые операторы поддерживаются в JavaScript:

  • op1 & op2- ANDОператор сравнивает два бита и генерирует результат 1, если оба бита равны 1; в противном случае возвращается 0.

  • op1 | op2- ORОператор сравнивает два бита и выдает результат 1, если биты дополняют друг друга; в противном случае возвращается 0.

  • op1 ^ op2- EXCLUSIVE-ORОператор сравнивает два бита и возвращает 1, если один из битов равен 1, и дает 0, если оба бита равны 0 или 1.

  • ~op1- COMPLEMENTОператор используется для инвертирования всех битов операнда.

  • op1 << op2- SHIFT LEFTОператор перемещает биты влево, отбрасывает крайний левый бит и присваивает крайнему правому биту значение 0. Каждое перемещение влево эффективно умножает op1 на 2.

  • op1 >> op2- SHIFT RIGHTОператор перемещает биты вправо, отбрасывает крайний правый бит и присваивает крайнему левому биту значение 0. Каждое движение вправо эффективно делит op1 пополам. Самый левый бит знака сохраняется.

  • op1 >>> op2- Оператор SHIFT RIGHT- ZERO FILLперемещает биты вправо, отбрасывает крайний правый бит и присваивает крайнему левому биту значение 0. Каждое перемещение вправо фактически делит op1 пополам. Самый левый бит знака отбрасывается.

Джефф Хиллман
источник
"если биты дополняют друг друга" - что?
Андрей Тюкин
@AndreyTyukin: два бита дополняют друг друга, если один из них равен 1, а другой - 0.
Джефф Хиллман
@JeffHillman Согласно вашему описанию в комментарии, 1 и 1 не являются «дополнительными». Тогда мне непонятно, почему 1 | 1дает, 1а не нет 0, и чем |тогда должно отличаться от ^. Несколько дней назад мне пришлось использовать этот вопрос / ответ в качестве дублирующей цели, и мне хотелось, чтобы через 10 лет у кого-то был более четкий канонический дубликат для такого рода вопросов.
Андрей Тюкин
4

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

Например (в десятичном формате):
х = 8
у = 1

выйдет (в двоичном формате):
х = 1000
у = 0001

Оттуда вы можете выполнять вычислительные операции, такие как «и» или «или»; в таком случае:
х | y =
1000 
0001 |
------
1001

или ... 9 в десятичной системе

Надеюсь это поможет.

javamonkey79
источник
|такое ИЛИ операции?
Si8, 03
По некоторым причинам это имело для меня наибольший смысл. До сих пор не уверен в x | y = 1000 0001 |части , хотя
samayo
4

Когда упоминается термин «поразрядный», иногда уточняется, что это не «логический» оператор.

Например, в JavaScript побитовые операторы обрабатывают свои операнды как последовательность из 32 бит (нулей и единиц) ; Между тем, логические операторы обычно используются с логическими (логическими) значениями, но могут работать с не-логическими типами.

Возьмем, к примеру, expr1 && expr2.

Возвращает expr1, если его можно преобразовать в false; в противном случае возвращает expr2. Таким образом, при использовании с логическими значениями && возвращает истину, если оба операнда истинны; в противном случае возвращает false.

a = "Cat" && "Dog"     // t && t returns Dog
a = 2 && 4     // t && t returns 4

Как отмечали другие, 2 и 4 - это побитовое И, поэтому оно вернет 0.

Вы можете скопировать следующее в test.html или что-то еще и протестировать:

<html>
<body>
<script>
    alert("\"Cat\" && \"Dog\" = " + ("Cat" && "Dog") + "\n"
        + "2 && 4 = " + (2 && 4) + "\n"
        + "2 & 4 = " + (2 & 4));
</script>
Юджин Йокота
источник
3

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

операции :

  • побитовое И

  • побитовое ИЛИ

  • побитовое НЕ

  • побитовое XOR

  • и т.д

Элемент списка

    AND|0 1        OR|0 1 
    ---+----      ---+---- 
      0|0 0         0|0 1 
      1|0 1         1|1 1 

   XOR|0 1        NOT|0 1 
   ---+----       ---+--- 
     0|0 1           |1 0 
     1|1 0

Например.

    203: 1100 1011
AND  15: 0000 1111
------------------
  =  11: 0000 1011

Использование побитового оператора

  • Операторы сдвига влево и вправо эквивалентны умножению и делению на x * 2 y соответственно.

Например.

int main()
{
     int x = 19;
     printf ("x << 1 = %d\n" , x <<1);
     printf ("x >> 1 = %d\n", x >>1);
     return 0;
}
// Output: 38 9
  • Оператор & можно использовать для быстрой проверки четности или нечетности числа.

Например.

int main()
{
    int x = 19;
    (x & 1)? printf("Odd"): printf("Even");
    return 0;
 }
// Output: Odd
  • Быстрый поиск минимум x и y без if elseинструкции

Например.

int min(int x, int y)
{
    return y ^ ((x ^ y) & - (x < y))
}
  • Преобразование десятичных чисел в двоичные

Например.

#include <stdio.h>
int main ()
{
    int n , c , k ;
    printf("Enter an integer in decimal number system\n " ) ;
    scanf( "%d" , & n );
    printf("%d in binary number
    system is: \n " , n ) ;
    for ( c = 31; c >= 0 ; c -- )
    {
         k = n >> c ;
         if ( k & 1 )
              printf("1" ) ;
         else
              printf("0" ) ;
      }
      printf(" \n " );
      return 0 ;
}
  • Шифрование с помощью шлюза XOR - популярный метод из-за его универсальности и повторного использования программистами.
  • Побитовый оператор XOR - самый полезный оператор с точки зрения технического собеседования.

побитовое смещение работает только с положительным числом

Также существует широкий спектр использования побитовой логики.

Prashant
источник
"сложность и приподнятость ..."?
Джонатан Кросс,
The left-shift and right-shift operators are equivalent to multiplication and division by x * 2y respectively.Это правильно! muyiy.cn/question/program/102.html
xgqfrms
мое решение repl.it/@xgqfrms/…
xgqfrms
1

Это может помочь думать об этом так. Вот как работает AND (&):

Он в основном говорит, что оба этих числа едины, поэтому, если у вас есть два числа 5 и 3, они будут преобразованы в двоичные, и компьютер будет думать

         5: 00000101
         3: 00000011

оба едины: 00000001 0 - ложь, 1 - истина

Итак, И 5 и 3 равно единице. Оператор OR (|) делает то же самое, за исключением того, что для вывода 1 только одно из чисел должно быть равно единице, а не обоим.

user3677963
источник
-5

Я все время слышал о том, насколько медленными были побитовые операторы JavaScript. Я провел несколько тестов для моего последнего сообщения в блоге и обнаружил, что они были на 40-80% быстрее, чем арифметическая альтернатива в нескольких тестах. Возможно, раньше они были медленными. В современных браузерах я их люблю.

В моем коде есть один случай, который из-за этого будет быстрее и легче читать. Я буду держать глаза открытыми для большего.

Nosredna
источник