Cuando programamos en Go, las expresiones y operadores aritméticos nos permiten ir más allá de asignar valores constantes a nuestras variables. Podemos construir fórmulas que hagan que nuestras asignaciones sean más dinámicas y útiles. Por ejemplo, podemos crear variables como x y y y asignarles valores numéricos literales, como 4 y 2 respectivamente. Pero también podemos asignar a una variable el valor de otra, como hacer que z tome el valor de x, que en ese momento es 4.
Lo interesante es que podemos combinar variables y literales en expresiones con operadores aritméticos para realizar cálculos. Si queremos que una variable sea el resultado de una suma, simplemente usamos el operador + entre los operandos, que pueden ser variables o números literales. Por ejemplo, p1 := x + y sumará 4 y 2, asignando 6 a p1. De igual forma, podemos usar la resta con -, la multiplicación con * y la división con /. Es importante recordar que la división entre enteros y entre flotantes puede comportarse de manera diferente, aunque eso es un tema más avanzado.
Como en matemáticas, los operadores tienen una precedencia que debemos respetar. Primero se realizan las multiplicaciones y divisiones, y luego las sumas y restas. Por ejemplo, en la expresión p8 := p1 + p4 * p5, primero se multiplica p4 por p5 y luego se suma p1. Si queremos cambiar ese orden, podemos usar paréntesis para agrupar operaciones, como en (p1 + p2) * p3, donde primero se suma p1 y p2, y luego se multiplica el resultado por p3.
Además de los operadores básicos, Go incluye el operador módulo %, que devuelve el residuo de una división. Para números positivos, funciona como el resto de una división euclidiana. Por ejemplo, 5 % 3 da 2, porque 5 dividido entre 3 da un cociente de 1 y un resto de 2. Sin embargo, con números negativos el comportamiento puede variar, por lo que se habla de módulo en lugar de resto.
También podemos trabajar con operadores binarios que manipulan los bits de los operandos. Estos operadores actúan a nivel de bits, ya que internamente los números se representan en binario. El operador AND & compara bit a bit y devuelve 1 solo si ambos bits son 1. El operador OR | devuelve 1 si al menos uno de los bits es 1. El operador XOR ^ devuelve 1 solo si los bits son diferentes. Por ejemplo, p1 & 3 hará un AND bit a bit entre p1 y 3.
El operador NOT binario también está disponible y es un operador unario, es decir, se aplica a un solo operando. Se escribe con el símbolo ^ antes de la variable o expresión, y niega todos los bits, convirtiendo los 1 en 0 y viceversa.
Finalmente, Go permite realizar desplazamientos de bits hacia la izquierda o hacia la derecha. Para desplazar hacia la izquierda usamos << y para la derecha >>. Estos operadores mueven los bits del número el número de posiciones que indiquemos, lo que equivale a multiplicar o dividir por potencias de dos, respectivamente.
Para ilustrar algunos de estos conceptos, podemos ver ejemplos de código en Go:
package main
import "fmt"
func main() {
x := 4
y := 2
z := x // z es 4
p1 := x + y // p1 es 6
p2 := x - y // p2 es 2
p3 := x * y // p3 es 8
p4 := x / y // p4 es 2 (división entera)
p5 := 5 % 3 // p5 es 2 (módulo)
// Precedencia de operadores
p6 := p1 + p2 * p3 // primero p2 * p3, luego suma p1
p7 := (p1 + p2) * p3 // primero suma p1 + p2, luego multiplica por p3
// Operadores binarios
andResult := p1 & 3 // AND bit a bit
orResult := p1 | 3 // OR bit a bit
xorResult := p1 ^ 3 // XOR bit a bit
notResult := ^p1 // NOT bit a bit
// Desplazamientos de bits
leftShift := p1 << 1 // desplaza bits a la izquierda (multiplica por 2)
rightShift := p1 >> 1 // desplaza bits a la derecha (divide por 2)
fmt.Println("z:", z)
fmt.Println("p1:", p1)
fmt.Println("p2:", p2)
fmt.Println("p3:", p3)
fmt.Println("p4:", p4)
fmt.Println("p5:", p5)
fmt.Println("p6:", p6)
fmt.Println("p7:", p7)
fmt.Println("AND:", andResult)
fmt.Println("OR:", orResult)
fmt.Println("XOR:", xorResult)
fmt.Println("NOT:", notResult)
fmt.Println("Left Shift:", leftShift)
fmt.Println("Right Shift:", rightShift)
}
Con estos operadores y expresiones podemos construir cálculos complejos y manipular datos a nivel binario, lo que amplía mucho las posibilidades de nuestros programas en Go.