Ответ 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 термин "Побитовое руководство по операциям", есть много хороших статей.