Saltar al contenido

Operaciones y conversiones de tipo

Al terminar esta lección vas a poder hacer cuentas con tus variables, entender por qué en Go 7 / 2 da 3 y convertir un valor de un tipo a otro, por ejemplo de int a float64. También vas a saber leer el error que aparece cuando mezclas dos tipos en una cuenta, y el que aparece cuando un número con decimales no cabe en un int.

Hacer cuentas

A la Papelería Central le llega un pedido: 3 cajas de 12 cuadernos y 5 cuadernos sueltos. ¿Cuántos cuadernos llegan, y cuántos quedan después de vender 8?

main.go
package main

import "fmt"

func main() {
	cajas := 3
	porCaja := 12
	sueltos := 5
	total := cajas*porCaja + sueltos
	fmt.Println("Cuadernos que llegan:", total)
	vendidos := 8
	fmt.Println("Quedan:", total-vendidos)
}

Salida:

Cuadernos que llegan: 41
Quedan: 33

Los operadores aritméticos hacen cuentas con números:

  • + suma y - resta.
  • * multiplica.
  • / divide y % da el resto de una división. Los ves en la sección siguiente.

cajas*porCaja + sueltos vale 41: Go multiplica primero (3 por 12 da 36) y después suma los 5 sueltos. Como en matemática, *, / y % se calculan antes que + y -. Entre operadores del mismo nivel, Go va de izquierda a derecha: 24 - 3 + 2 da 23.

El resultado de una cuenta es un valor como cualquier otro: puedes guardarlo en una variable, como total, o pasarlo directo a fmt.Println, como total-vendidos, que da 33.

Para que una suma vaya antes que una multiplicación, usa paréntesis. Mateo pide 2 kits escolares, y cada kit trae 3 cuadernos y 4 lápices:

main.go
package main

import "fmt"

func main() {
	kits := 2
	cuadernos := 3
	lapices := 4
	fmt.Println(kits * (cuadernos + lapices))
	fmt.Println(kits*cuadernos + lapices)
}

Salida:

14
10

La primera línea da 14, la cuenta correcta: el paréntesis hace la suma primero (7 artículos por kit) y después Go multiplica por 2. La segunda, sin paréntesis, da 10: Go multiplica 2 por 3 y le suma 4, como si los lápices no vinieran en cada kit.

La regla: lo que está entre paréntesis se calcula primero; después, *, / y %; al final, + y -.

Consejo

Fíjate en los espacios de cajas*porCaja + sueltos: así escribe las cuentas gofmt, la herramienta que da el formato oficial de Go. Pega los operadores que se calculan primero y separa los demás, para que el orden se vea a simple vista. Dentro de una llamada con varios valores, gofmt también junta la cuenta: por eso el ejemplo escribe fmt.Println("Quedan:", total-vendidos), sin espacios alrededor del -. Escribe tus cuentas igual.

División entera y resto

La papelería guarda 20 lápices en estuches de 6. ¿Cuántos estuches llena y cuántos lápices sobran?

main.go
package main

import "fmt"

func main() {
	lapices := 20
	porEstuche := 6
	fmt.Println("Estuches:", lapices/porEstuche)
	fmt.Println("Sobran:", lapices%porEstuche)
}

Salida:

Estuches: 3
Sobran: 2

Entre dos enteros, / hace una división entera: da solo la parte entera del resultado y descarta los decimales. La cuenta exacta daría 3 y un tercio, pero lapices/porEstuche da 3: tres estuches llenos. Por la misma razón, 7 / 2 da 3, no 3.5.

% da el resto de esa división: lo que sobra. lapices%porEstuche da 2, porque tres estuches de 6 usan 18 lápices y quedan 2. % solo funciona con enteros.

La regla: entre dos int, / da un int, sin decimales. Para que la división dé 3.5, los números tienen que ser float64, como ves en la sección siguiente.

Si vienes de JavaScript, Python o Java…

En JavaScript y en Python, 7 / 2 da 3.5: / siempre divide con decimales. Python tiene además // para la división entera. En Go, como en Java, / entre dos enteros da un entero: 7 / 2 da 3.

Ojo con los negativos si vienes de Python: Go descarta los decimales, así que -7 / 2 da -3. En Python, -7 // 2 redondea hacia abajo y da -4.

El tipo que deduce :=

Una caja de 6 lápices cuesta 9. ¿Cuánto cuesta cada lápiz? Depende de cómo escribas el precio:

main.go
package main

import "fmt"

func main() {
	precioCaja := 9
	fmt.Println(precioCaja / 6)
	precioExacto := 9.0
	fmt.Println(precioExacto / 6)
}

Salida:

1
1.5

:= deduce el tipo del valor, como viste en la lección anterior. precioCaja es un int, porque 9 no tiene decimales, así que precioCaja / 6 es una división entera y da 1. precioExacto es un float64, porque 9.0 tiene punto decimal (aunque el decimal sea cero), y la división da 1.5.

El resultado de una cuenta tiene el tipo de los valores que operan. Si lo guardas con :=, la variable nueva recibe ese tipo: porLapiz := precioExacto / 6 declara un float64, y estuches := lapices / 6 declara un int.

¿Y el 6? Un número escrito tal cual en el código toma el tipo que necesita la cuenta, si su valor cabe en ese tipo. En precioExacto / 6, el 6 funciona como un float64.

Al revés también: en precioCaja / 6.0, el 6.0 cabe en un int y funciona como un int, así que la división da 1. Escribir .0 da un float64 cuando Go deduce el tipo de una variable nueva, como con :=, pero no dentro de una cuenta con un int. Lo verás a fondo en la lección Constantes e iota.

La regla: el tipo de una variable decide cómo calcula Go. Si un valor puede tener decimales, como un precio, dale el tipo float64 desde el principio: escribe 9.0 en lugar de 9, o declara con var precioCaja float64 = 9.

Mezclar tipos en una cuenta

Un lápiz cuesta 1.25, y Camila compra 3. El precio es un float64 y la cantidad, un int:

main.go
package main

import "fmt"

func main() {
	precio := 1.25
	cantidad := 3
	total := precio * cantidad
	fmt.Println(total)
}

Salida:

./main.go:8:11: invalid operation: precio * cantidad (mismatched types float64 and int)

Lee el mensaje por partes:

  • ./main.go:8:11 es el lugar: archivo main.go, línea 8, columna 11, donde empieza la cuenta.
  • invalid operation significa “operación no válida”.
  • precio * cantidad es la cuenta que falla.
  • (mismatched types float64 and int) significa “tipos que no coinciden: float64 e int”.

Go no hace una cuenta entre dos tipos distintos, y no convierte uno al otro por su cuenta. Si eligiera por ti, a veces perdería decimales sin avisarte. Por eso te pide que conviertas tú uno de los dos valores, como ves en la sección «Convertir con T(x)».

Si vienes de JavaScript, Python o Java…

En JavaScript, todos los números son del tipo number, así que la cuenta funciona. En Python, 1.25 * 3 da 3.75: Python pasa el entero a decimal sin preguntar. Java hace lo mismo con un double y un int. Go nunca convierte por su cuenta una variable de un tipo numérico a otro.

Lo mismo pasa con un texto y un número: en JavaScript, "Total: " + 3.75 los une, y en Go, "Total: " + total no compila. Para imprimirlos juntos, sepáralos con una coma en fmt.Println.

Unir textos con +

Con dos strings, + no suma: los une. La papelería arma la etiqueta de un producto:

main.go
package main

import "fmt"

func main() {
	producto := "cuaderno"
	etiqueta := "Producto: " + producto
	etiqueta += " (en oferta)"
	fmt.Println(etiqueta)
}

Salida:

Producto: cuaderno (en oferta)

"Producto: " + producto da "Producto: cuaderno": el texto de la derecha queda pegado al de la izquierda, sin ningún espacio de más. Por eso el espacio va dentro de las comillas, en "Producto: ". etiqueta += " (en oferta)" es una forma corta de escribir etiqueta = etiqueta + " (en oferta)": agrega el texto al final de etiqueta.

Los dos lados de + tienen que ser string. Con un número, como en "Total: " + total cuando total es un float64, el programa no compila: Go responde con mismatched types untyped string and float64, el mismo tipo de error de esta sección. Para imprimir un texto junto a un número, pásalos a fmt.Println separados por una coma: fmt.Println("Total:", total).

Convertir con T(x)

Para que compile la cuenta de Camila, los 3 lápices de 1.25, convierte la cantidad a float64:

main.go
package main

import "fmt"

func main() {
	precio := 1.25
	cantidad := 3
	total := precio * float64(cantidad)
	fmt.Println("Total:", total)
	fmt.Println("Cantidad:", cantidad)
}

Salida:

Total: 3.75
Cantidad: 3

float64(cantidad) es una conversión: toma el valor de cantidad y lo da como un float64. Ahora los dos lados de * son float64, y total vale 3.75.

La forma general es T(x): el nombre del tipo al que quieres llegar (T) y, entre paréntesis, el valor que conviertes (x). float64(cantidad) convierte a float64, e int(precio) convierte a int.

Convertir no cambia la variable: cantidad sigue siendo un int y sigue valiendo 3, como muestra la segunda línea. La conversión da un valor nuevo, que se usa en la cuenta.

El momento en que conviertes importa. Con la caja de 9 de la sección anterior:

main.go
package main

import "fmt"

func main() {
	precioCaja := 9
	lapices := 6
	fmt.Println(float64(precioCaja) / float64(lapices))
	fmt.Println(float64(precioCaja / lapices))
}

Salida:

1.5
1

La primera línea convierte los dos valores antes de dividir, y la división da 1.5. La segunda divide primero: precioCaja / lapices es una división entera, que da 1, y convertir ese 1 a float64 ya no recupera los decimales. Para no perderlos, convierte antes de dividir.

Entre números, T(x) convierte de un tipo a otro. Un texto no se convierte así: para pasar "24" a 24 se usa el paquete strconv, que verás en la lección “La biblioteca estándar”.

Si vienes de JavaScript, Python o Java…

En Python, int("24") convierte un texto en número, y str(24), un número en texto. En JavaScript, Number("24") y String(24) hacen lo mismo. En Go, int("24") no compila, y string(65) da "A" (el carácter con el código 65), no "65". Para pasar entre texto y número está el paquete strconv.

De float64 a int: los decimales se pierden

Un rollo de cinta de regalo tiene 3.9 metros. ¿Cuántos trozos de un metro completo puedes cortar?

main.go
package main

import "fmt"

func main() {
	cinta := 3.9
	metros := int(cinta)
	fmt.Println("Metros completos:", metros)
	fmt.Println("Cinta en el rollo:", cinta)
}

Salida:

Metros completos: 3
Cinta en el rollo: 3.9

int(cinta) convierte 3.9 a int, y el resultado es 3: la conversión descarta los decimales. No redondea: aunque 3.9 está más cerca de 4, da 3. Con un número negativo pasa lo mismo: -3.9 da -3. Y cinta no cambia: sigue valiendo 3.9.

Convertir de float64 a int pierde información. Hazlo solo cuando quieras quedarte con la parte entera, como aquí, donde importan los metros completos.

Con un número escrito tal cual en el código, Go no te deja perder decimales sin darte cuenta. Si declaras el precio de un cuaderno como int, el programa no compila:

main.go
package main

import "fmt"

func main() {
	var precio int = 2.75
	fmt.Println(precio)
}

Salida:

./main.go:6:19: cannot use 2.75 (untyped float constant) as int value in variable declaration (truncated)

Lee el mensaje por partes:

  • ./main.go:6:19 es el lugar: archivo main.go, línea 6, columna 19, donde empieza 2.75.
  • cannot use 2.75 significa “no se puede usar 2.75”.
  • (untyped float constant) dice qué es 2.75 para Go: un número con decimales escrito tal cual en el código. float viene de floating point, como en float64.
  • as int value in variable declaration significa “como valor int en la declaración de una variable”.
  • (truncated) significa “truncado”: para caber en un int, 2.75 perdería sus decimales.

Un precio lleva decimales, así que su tipo es float64: var precio float64 = 2.75. Si de verdad querías un entero, escríbelo sin decimales.

Tampoco compila int(3.9) con el número escrito así: Go responde cannot convert 3.9 (untyped float constant) to type int. int(…) descarta los decimales de una variable, como cinta, pero no los de un número escrito en el código.

Consejo

Antes de convertir a int, pregúntate si los decimales importan. Un precio o un promedio necesitan float64 de principio a fin. Convierte a int cuando cuentas unidades completas, como los metros de cinta, y hazlo al final, después de las cuentas con decimales.

Si vienes de JavaScript, Python o Java…

En Python, int(3.9) también da 3, y en Java, (int) 3.9 también. En JavaScript, Math.trunc(3.9) hace lo mismo. La diferencia está en el número escrito tal cual: en Go, int(3.9) no compila, y la conversión solo descarta decimales de una variable.

En resumen

  • +, -, *, / y % hacen cuentas. *, / y % van antes que + y -, y los paréntesis cambian el orden.
  • Entre dos enteros, / descarta los decimales (7 / 2 da 3) y % da el resto.
  • := deduce el tipo del valor (9 es un int y 9.0, un float64), y el tipo decide cómo calcula Go.
  • Go no hace cuentas entre tipos distintos (mismatched types): convierte uno de los valores con T(x), como float64(cantidad), antes de operar.
  • int(x) descarta los decimales de un float64, sin redondear: 3.9 da 3.

Ejercicios

Debajo de Sueltos: 5, imprime el total de cuadernos del pedido, calculado con cajas, porCaja y sueltos: Total: 41.

  main.go

Para salir del editor con el teclado, pulsa Escape y luego Tab.

package main

import "fmt"

func main() {
	cajas := 3
	porCaja := 12
	sueltos := 5
	fmt.Println("Cajas:", cajas, "de", porCaja)
	fmt.Println("Sueltos:", sueltos)
	// Escribe aquí: calcula el total de cuadernos e imprímelo.
}
 

¿Ver la solución?

La solución reemplazará tu código en el editor. Antes de verla, prueba con una pista: se aprende más cuando lo resuelves tú.