math - Diferencia entre algunos operadores “|”, “^”, “&”, “& ^”. Golang
operators bitwise-operators (2)
Los operadores bitwise entran en juego cuando tienes que trabajar con datos a nivel de bit o de byte .
Aquí enumero un puñado de ejemplos que utilizan operaciones de bits con ejemplos de código (en ningún orden en particular):
1. Son comunes y forman parte de muchos algoritmos en criptografía y funciones hash (por ejemplo, MD5 ).
2. También se usan a menudo si desea "ahorrar" espacio y empacar múltiples variables "bool" en un int
Por ejemplo, asigna un bit a cada variable bool. Tiene que usar operadores bitwise para poder cambiar / leer individualmente los bits.
Por ejemplo, empaquetando 8 bits / bools en un 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. Otra área es la compresión de datos, donde desea aprovechar al máximo un byte
y utilizar todos sus bits para almacenar / recuperar cierta información (un bit es la unidad básica de información en computación y comunicaciones digitales).
4. Similar a la compresión pero no exactamente lo mismo: bitstreams . También se usa para ahorrar espacio en un flujo de datos al no enviar bytes completos, sino campos que tienen una longitud de bits arbitraria.
He escrito y publicado un paquete de Lector y Escritor altamente optimizado a nivel de bits, abierto aquí: github.com/icza/bitio . Verá un uso extensivo de todo tipo de operaciones de bit en sus fuentes.
5. Otro uso práctico: probar ciertas propiedades de un número (entero) . Al conocer la representación binaria de números enteros ( el complemento de dos ), hay ciertas características de los números en su representación binaria. Por ejemplo, un número entero (en el complemento de 2) es par (puede dividirse por 2) si el bit más bajo es 0:
func isEven(i int) bool {
return i&0x01 == 0
}
Al probar los bits de un número entero, también puede saber si es una potencia de 2. Por ejemplo, si un número positivo solo contiene un bit 1
, entonces es una potencia de 2 (por ejemplo, 2 = 0x02 = 00000010b
, 16 = 0x10 = 00010000
pero por ejemplo 17 = 0x11 = 00010001
no potencia de 2).
6. Muchos procedimientos de codificación / decodificación también utilizan operaciones de bit. La más trivial es la codificación UTF-8 que utiliza una codificación de longitud variable para representar puntos de código Unicode ( rune
en Go) como secuencias de bytes.
Una simple variación de una codificación de longitud variable podría ser usar el bit más alto de un byte (8 o 7 si el índice es 0) para indicar si se requieren más bytes para decodificar un número, y los 7 bits restantes siempre son "útiles". "datos. Puede probar el bit más alto y "separar" los 7 bits útiles como este:
b := readOneByte()
usefulBits := b & 0x7f
hasMoreBytes := b & 0x80 != 0
El beneficio de usar dicha codificación de longitud variable es que incluso si usa el tipo uint64
en Go, que tiene 8 bytes en la memoria, los números pequeños pueden representarse usando menos bytes (los números en el rango 0..127
solo requieren 1 byte. ). Si las muestras que desea almacenar o transferir tienen muchos valores pequeños, solo esto puede comprimir los datos a 1/8 = 12.5%. El lado negativo es que los números grandes (que tienen bits incluso en el byte más alto) usarán más de 8 bytes. Si vale la pena, depende de la heurística de las muestras.
X. Y la lista sigue ...
¿Puede vivir sin saber / usar operadores bitwise en Go (y en muchos otros lenguajes de programación)? La respuesta es sí. Pero si los conoce, a veces pueden hacer su vida más fácil y sus programas más eficientes.
Si desea obtener más información sobre el tema, lea el artículo de Wikipedia: Operación Bitwise y busque en Google el término "Tutorial de operadores de Bitwise", hay muchos artículos buenos.
Recientemente leí las especificaciones de golang y me encontré con algunos operadores interesantes:
& bitwise AND integers
| bitwise OR integers
^ bitwise XOR integers
&^ bit clear (AND NOT) integers
He intentado jugar con él, pero el único que he entendido es que "|" agregue enteros y el operador "+" adicionalmente trabaja con flotadores, cadenas, etc.
¿Para qué se usan en la práctica? ¿Alguien podría dar alguna explicación sobre estos 4 operadores arriba?
Por lo que técnicamente hacen, echa un vistazo a los comentarios en este
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)
}
Tenga en cuenta que di dos ejemplos de |
para demostrar que no es realmente la suma como 1 + 5
.
En cuanto a los usos prácticos, estoy seguro de que otros pueden comentar con más ejemplos, pero un uso común es crear una máscara de bits de indicadores para algo como un sistema de permisos.