Разница между некоторыми операторами "|", "^", "&", "& ^". Golang

Недавно я прочитал спецификацию golang и столкнулся с некоторыми интересными операторами:

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

Я пытался играть с ним, но единственное, что я понял, это то, что "|" добавлять целые числа и оператор "+" дополнительно работать с поплавками, строками и т.д.

Что они используют на практике? Может ли кто-нибудь дать объяснение об этих 4 операторах выше?

Ответы

Ответ 1

Побитовые операторы вступают в игру, когда вам приходится работать с данными байтового или битового уровня.

Здесь я перечисляю несколько примеров, используя битовые операции с образцами кода (в определенном порядке):


1.. Они являются общими и являются частью многих алгоритмов в криптографических и хеш-функциях (например, MD5).

2. Они также часто используются, если вы хотите "сохранить" пространство и, например, упаковать несколько переменных "bool" в один int, вы назначаете бит каждой переменной bool. Вы должны использовать побитовые операторы, чтобы иметь возможность индивидуально изменять/читать биты.

Например, упакуйте 8 бит /bools в один int:

flags := 0x00  // All flags are 0
flags |= 0x02  // Turn the 2nd bit to 1 (leaving rest unchanged)
flags |= 0xff  // Turn 8 bits (0..7) to 1
flags &= 0xfe  // Set the lowest bit to 0 (leaving rest unchanged)

istrue := flags&0x04 != 0 // Test if 3rd bit is 1

3. Другая область - это сжатие данных, в которых вы хотите получить максимальную отдачу от byte, и использовать все свои биты для хранения/восстановления некоторой информации (бит - это базовая единица информации в вычислительной и цифровой связи).

4. Подобно сжатию, но не совсем то же: битовые потоки. Он также используется для экономии места в потоке данных, не отправляя полные байты, а скорее поля с произвольной длиной бит.

Я написал и опубликовал высоко оптимизированный пакет Reader и Writer на уровне бит, открытый здесь: github.com/icza/bitio. Вы увидите широкое использование всех видов бит-операций в своих источниках.

5. Другое практическое использование: тестирование определенных свойств (целочисленного) числа. Зная двоичное представление целочисленных чисел (Два дополнения), в их двоичном представлении есть определенные характеристики чисел. Например, целое число (в 2 дополнениях) четное (можно разделить на 2), если младший бит равен 0:

func isEven(i int) bool {
    return i&0x01 == 0
}

Проверяя биты целого числа, вы также можете сказать, имеет ли он силу 2. Например, если положительное число содержит только один бит 1, то это сила 2 (например, 2 = 0x02 = 00000010b, 16 = 0x10 = 00010000 но, например, 17 = 0x11 = 00010001 не имеет значения 2).

6. Многие процедуры кодирования/декодирования также используют битовые операции. Наиболее тривиальным является кодировка UTF-8, которая использует кодировку переменной длины для представления кодовых точек Unicode (rune в Go) в качестве последовательностей байтов.
Простой вариант кодирования с переменной длиной может состоять в том, чтобы использовать старший бит байта (8-й или 7-й, если 0-индексированный), чтобы сигнализировать, требуется ли больше байтов для декодирования числа, а остальные 7 бит всегда являются "полезными" данные. Вы можете протестировать самый старший бит и "разделить" 7 полезных битов следующим образом:

b := readOneByte()
usefulBits := b & 0x7f
hasMoreBytes := b & 0x80 != 0

Прибыль от использования такой кодировки переменной длины заключается в том, что даже если вы используете тип uint64 типа Go, который имеет 8 байтов в памяти, небольшие числа могут быть представлены с меньшим количеством байтов (только цифры в диапазоне 0..127 требуется 1 байт!). Если образцы, которые вы хотите сохранить или передать, имеют много небольших значений, это само по себе может сжать данные до 1/8-го = 12,5%. Нижняя сторона состоит в том, что большие числа (которые имеют биты даже в самом высоком байте) будут использовать более 8 байтов. Стоит ли это того, что это зависит от эвристики образцов.

X. И этот список продолжается...


Можете ли вы жить, не зная/используя побитовые операторы в Go (и во многих других языках программирования)? Ответ: Да. Но если вы их знаете, иногда они могут сделать вашу жизнь проще и ваши программы более эффективными.

Если вы хотите узнать больше о теме, прочитайте статью Википедии: Побитовая операция и google термин "Побитовое руководство по операциям", есть много хороших статей.

Ответ 2

Для чего они технически проверяют комментарии в этом

package main

import "fmt"

func main() {
    // Use bitwise OR | to get the bits that are in 1 OR 2
    // 1     = 00000001
    // 2     = 00000010
    // 1 | 2 = 00000011 = 3
    fmt.Println(1 | 2)

    // Use bitwise OR | to get the bits that are in 1 OR 5
    // 1     = 00000001
    // 5     = 00000101
    // 1 | 5 = 00000101 = 5
    fmt.Println(1 | 5)

    // Use bitwise XOR ^ to get the bits that are in 3 OR 6 BUT NOT BOTH
    // 3     = 00000011
    // 6     = 00000110
    // 3 ^ 6 = 00000101 = 5
    fmt.Println(3 ^ 6)

    // Use bitwise AND & to get the bits that are in 3 AND 6
    // 3     = 00000011
    // 6     = 00000110
    // 3 & 6 = 00000010 = 2
    fmt.Println(3 & 6)  

    // Use bit clear AND NOT &^ to get the bits that are in 3 AND NOT 6 (order matters)
    // 3      = 00000011
    // 6      = 00000110
    // 3 &^ 6 = 00000001 = 1
    fmt.Println(3 &^ 6)
}

Посмотрите на игровой площадке

Обратите внимание, что я дал два примера |, чтобы показать, что это не дополнение, например 1 + 5.

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