Skip to content
This page has been auto-translated and may contain errors.View in English

Números y aritmética

Los números aparecen en casi todos los programas que escribes. Un carrito de compras totaliza un precio. Un juego actualiza un puntaje. Un script cuenta cuántas veces ocurrió algo. Python te da operadores aritméticos que funcionan como las matemáticas en papel, además de algunos más que vale la pena conocer desde el principio.

Los operadores aritméticos de Python cubren el conjunto estándar más la división entera, el módulo y la exponenciación. Algunos comportamientos difieren de otros lenguajes en formas que importan en la práctica: / siempre devuelve un float, la división entera redondea hacia el infinito negativo, y el módulo sigue la semántica de módulo verdadero.

La torre numérica de Python: int (precisión arbitraria), float (IEEE 754 binary64), complex (no se cubre aquí). Los operadores aritméticos siguen definiciones matemáticas en lugar de la convención de C: // es división entera (hacia el infinito negativo), % lleva el signo del divisor, y ambos juntos cumplen la identidad a == (a // b) * b + (a % b) para todas las entradas enteras.

Los operadores

Los cuatro operadores de las matemáticas (+, -, *, /) funcionan exactamente como esperarías. Python añade tres más que usarás constantemente: división entera, residuo y exponenciación.

Los cuatro operadores estándar se comportan como se espera, con una regla notable: / siempre devuelve un float, incluso cuando el resultado es un número entero. Los tres operadores adicionales amplían lo que puedes expresar sin trabajo extra.

Los siete operadores se mapean a métodos dunder: + a __add__, // a __floordiv__, % a __mod__, ** a __pow__, y así sucesivamente. Las operaciones mixtas int/float se ensanchan a float. / siempre devuelve float sin importar los tipos de los operandos.

python
price    = 12.99
quantity = 3

print(price * quantity)   # 38.97
print(price + 2)          # 14.99
print(price - 1.00)       # 11.99
OperadorNombreEjemploResultado
+Suma5 + 38
-Resta5 - 32
*Multiplicación5 * 315
/División5 / 31.6666...
//División entera5 // 31
%Residuo5 % 32
**Exponenciación5 ** 3125

División: / vs //

/ siempre te da el resultado decimal exacto, incluso si la respuesta es un número entero. // te da solo la parte entera, cortando todo lo que está después del punto decimal. No redondea; corta:

/ siempre devuelve un float, sin importar si las entradas son enteras. // devuelve el piso del resultado: el entero más grande que es menor o igual al resultado verdadero. Para números positivos eso es lo mismo que truncar. Para negativos, no:

/ es división verdadera: siempre devuelve float. // es división entera (floor division): aplica math.floor() al cociente verdadero, siempre redondeando hacia el infinito negativo en lugar de hacia cero. Esto difiere de C y Java, donde la división entera trunca. El beneficio matemático: // y % de Python cumplen la identidad a == (a // b) * b + (a % b) para todas las entradas enteras incluyendo negativos. La división truncante de C rompe esta identidad para negativos.

python
10 / 2     # 5.0   (siempre float, incluso cuando divide exactamente)
10 / 3     # 3.3333333333333335

10 // 3    # 3
7  // 2    # 3
-7 // 2    # -4    (redondea hacia el infinito negativo, no hacia cero)

El resultado de -7 // 2 sorprende a la gente. Casi siempre usarás // con números positivos donde esto no surge. Mantenlo en la mente para cuando aparezcan negativos.

Python llama a esto división entera (floor division) porque aplica la función matemática de piso. Otros lenguajes truncan hacia cero en su lugar, dando un resultado distinto para negativos. El nombre // es una pista: dividir, luego aplicar piso.

// implementa floor(a / b), no truncamiento. La identidad a == (a // b) * b + (a % b) se cumple para todas las entradas enteras en Python. En C y Java, donde / trunca hacia cero, esta identidad falla para valores negativos y % actúa como operador de residuo (toma el signo del dividendo) en lugar de módulo verdadero (toma el signo del divisor).

El operador de residuo %

% te da lo que queda después de la división entera. Si 10 // 3 es 3 (porque 3 cabe en 10 tres veces), entonces 10 % 3 es 1 (porque 3 × 3 = 9, y 10 - 9 = 1). El uso más común es verificar si un número es par o impar.

% es el operador de módulo. Verificar par/impar es el uso obvio, pero se generaliza a cualquier problema de ciclo o envoltura: mantener un contador dentro de un rango, distribuir elementos entre grupos, repetir una secuencia. El patrón es siempre value % limit, que devuelve algo entre 0 y limit - 1.

El % de Python es módulo verdadero: el resultado siempre lleva el signo del divisor. Esto difiere de C y Java donde % es un operador de residuo y toma el signo del dividendo. En Python, -7 % 3 es 2, no -1, porque el módulo se define como a - (a // b) * b y // redondea hacia el infinito negativo. Este comportamiento consistente del signo es lo que hace que % sea confiable para ciclos y envolturas con entradas negativas.

python
10 % 3    # 1
10 % 2    # 0  (divide exactamente)
10 % 7    # 3

6 % 2     # 0  (par)
7 % 2     # 1  (impar)

Exponenciación **

** eleva un número a una potencia. Usa dos asteriscos, no el símbolo ^ (que significa otra cosa en Python):

** es exponenciación. También funciona con floats, lo que te permite expresar raíces como potencias fraccionarias en lugar de una llamada a función separada:

** llama a __pow__. Con dos operandos int devuelve int; con cualquier operando float devuelve float. Una trampa de precedencia: -2 ** 2 se analiza como -(2 ** 2) porque ** se asocia más fuerte que el menos unario, dando -4, no 4. Usa paréntesis: (-2) ** 2.

python
2 ** 10    # 1024
3 ** 3     # 27
9 ** 0.5   # 3.0  (raíz cuadrada: elevar a la potencia 0.5)

Precedencia de operadores

Python sigue el orden estándar de las matemáticas: exponenciación primero, luego multiplicación y división, luego suma y resta. Cuando no estés seguro, usa paréntesis. Hacen clara la intención y no cuestan nada:

Python sigue el orden estándar PEMDAS/BODMAS. La parte que hace tropezar a la gente: /, //, y % comparten el mismo nivel de precedencia y se evalúan de izquierda a derecha cuando se mezclan. Los paréntesis son gratis; úsalos siempre que el orden no sea obvio a primera vista:

Precedencia de mayor a menor entre los operadores aritméticos: **, luego - unario, luego * / // % (de izquierda a derecha con igual precedencia), luego + -. La interacción del menos unario con ** es una trampa sutil: -2 ** 2 es -(2 ** 2) = -4 porque el menos unario se asocia más débilmente que **. Siempre coloca paréntesis al combinar negación con exponenciación.

python
2 + 3 * 4      # 14, no 20
2 ** 3 + 1     # 9,  no 512
10 - 4 / 2     # 8.0, no 3.0

(2 + 3) * 4    # 20
10 / (2 + 3)   # 2.0

Cómo interactúan int y float

Python tiene una regla consistente: / siempre devuelve un decimal (incluso 4 / 2 da 2.0), y cualquier operación que mezcle un entero con un decimal da un decimal. Cuando necesitas un número entero, usa // o convierte con int().

Las reglas de tipo son predecibles: / siempre devuelve float. // y % con dos enteros devuelven int. Cualquier operación que mezcle int y float devuelve float. Esto significa que 4 / 2 es 2.0, no 2, lo cual importa cuando necesitas un entero (por ejemplo, para usar como índice).

La coerción de tipos sigue una jerarquía fija: int se ensancha a float en operaciones mixtas. / se mapea a __truediv__, que siempre devuelve float. // se mapea a __floordiv__: con dos operandos int devuelve int; con cualquier operando float devuelve float. Estas reglas son consistentes y predecibles; la única sorpresa es que / nunca devuelve int ni siquiera para 4 / 2.

python
4 / 2      # 2.0   (float, siempre)
4 // 2     # 2     (int)
4 + 2      # 6     (int)
4 + 2.0    # 6.0   (float)
4 * 0.5    # 2.0   (float)

Precisión de los floats

Hay una trampa que sorprende a casi todos en algún momento:

python
0.1 + 0.2   # 0.30000000000000004

Ese pequeño error no es un bug de Python. Las computadoras almacenan los números decimales en binario, y algunos valores como 0.1 no se pueden representar exactamente. Es similar a cómo 1/3 no se puede escribir exactamente en decimal. Para la mayoría de los cálculos cotidianos no importa. Para mostrar dinero, round() o el especificador de formato :.2f mantendrán la salida ordenada.

Los floats de Python son IEEE 754 binary64: 64 bits con aproximadamente 15-16 dígitos decimales significativos de precisión. La imprecisión aparece porque algunas fracciones no se pueden representar exactamente en binario. 0.1 + 0.2 produce 0.30000000000000004. La desviación solo se muestra cuando inspeccionas el valor crudo; formatear con :.2f o round() lo oculta en la salida.

Para trabajo financiero donde las fracciones de centavo se acumulan, Python provee decimal.Decimal en la biblioteca estándar con aritmética exacta en base 10. Eso se cubre en el capítulo Módulos.

float es IEEE 754 binary64: signo × mantisa × 2^exponente con 53 bits de mantisa, dando una precisión relativa de 2^-52 ≈ 2.2e-16. Cualquier fracción cuyo denominador tenga factores primos distintos de 2 (como 1/10 = 1/(2×5)) es una fracción binaria no terminante y no puede almacenarse exactamente. El error es pequeño pero se acumula en aritmética repetida.

Para aritmética decimal exacta, el decimal.Decimal de Python usa internamente base 10 de precisión arbitraria. Para aritmética racional exacta sin redondeo alguno, fractions.Fraction almacena pares numerador/denominador. Ambos están en la biblioteca estándar, se cubren en el capítulo Módulos.

Literales numéricos legibles

Python te permite poner guiones bajos en los literales numéricos para hacer los números grandes más fáciles de leer. Python los ignora completamente; son solo para ti:

Los guiones bajos son válidos en cualquier parte de un literal numérico y se eliminan durante el análisis sin efecto sobre el valor. Útiles para separadores de miles en constantes y para agrupar dígitos en literales binarios o hexadecimales:

Los guiones bajos en literales numéricos son una característica del tokenizador: se eliminan durante el análisis léxico sin efecto alguno sobre el valor resultante. Válidos en enteros, floats y literales con base (0xFF_FF, 0b1010_0001, 1_234.567_890). Las únicas restricciones: no pueden aparecer al inicio, al final, o adyacentes a un punto decimal o marcador de exponente.

python
population  = 8_100_000_000
distance_km = 384_400
pi_approx   = 3.141_592_653

Funciones integradas útiles

abs()

abs() devuelve el valor absoluto: siempre positivo, sin importar el signo de la entrada. Úsala cuando te importa qué tan lejos está un número del cero, no en qué dirección.

abs() devuelve la magnitud de un número. Funciona con enteros y floats. Útil para cálculos de distancia, márgenes de error y cualquier situación donde la dirección sea irrelevante y solo necesites el tamaño del valor.

abs() llama a __abs__ sobre el operando. Para int y float devuelve el mismo tipo. Para complex devuelve la magnitud (distancia euclidiana al origen) como float. El tipo de retorno coincide con el tipo de entrada para los números reales.

python
abs(-5)     # 5
abs(3.7)    # 3.7
abs(-0.5)   # 0.5

round()

round() redondea al entero más cercano por defecto. Pasa un segundo argumento para mantener un número específico de decimales:

python
round(3.7)          # 4
round(3.2)          # 3
round(3.14159, 2)   # 3.14

Algo que vale la pena saber: round(2.5) da 2, no 3. Python redondea al número par más cercano cuando un valor está exactamente a la mitad entre dos opciones.

round() usa redondeo bancario: cuando el valor está exactamente a la mitad, redondea al número par más cercano en lugar de siempre redondear hacia arriba. Esto minimiza el error acumulado en trabajo estadístico pero puede sorprenderte si esperas que 0.5 siempre redondee hacia arriba:

python
round(2.5)   # 2  (redondea al par más cercano)
round(3.5)   # 4
round(4.5)   # 4  (no 5)
round(3.14159, 2)   # 3.14

round() implementa el redondeo IEEE 754 a-medio-par (redondeo bancario): los empates redondean al entero par más cercano. Esto difiere de la convención "redondear medio hacia arriba". Con un argumento ndigits, round() llama a __round__ sobre el objeto; los tipos personalizados pueden sobrescribir el comportamiento de redondeo. Nota: como los floats no son exactos, los "empates" como round(2.5) pueden no caer exactamente en 0.5 en binario, dando resultados que parecen inconsistentes.

python
round(2.5)   # 2
round(3.5)   # 4
round(4.5)   # 4

divmod()

divmod() te da el cociente y el residuo en una sola llamada. Devuelve un par de valores que puedes asignar a dos nombres a la vez:

divmod(a, b) es equivalente a (a // b, a % b) pero calculado en un solo paso. Úsala cuando necesites ambos valores de todos modos: paginación, conversión de tiempo, o distribución de elementos en grupos.

divmod() llama a __divmod__ sobre el operando izquierdo. Realiza la división una vez y devuelve tanto el cociente entero como el residuo del módulo, evitando el cálculo redundante de llamar a // y % por separado. El resultado satisface a == divmod(a, b)[0] * b + divmod(a, b)[1] con la semántica de división entera de Python para todas las entradas enteras.

python
divmod(10, 3)   # (3, 1): cociente 3, residuo 1
divmod(7, 2)    # (3, 1)
divmod(9, 3)    # (3, 0)

quotient, remainder = divmod(10, 3)
print(quotient)    # 3
print(remainder)   # 1

¿Qué es eso de (3, 1)?

Eso es una tupla: un par fijo de valores devueltos juntos. Las tuplas tienen su propio capítulo. Por ahora, separa los dos valores asignando a dos nombres a la vez, como se muestra arriba.

En la práctica

Una calculadora de propina:

python
bill     = 45.50
tip_rate = 0.18
tip      = round(bill * tip_rate, 2)
total    = round(bill + tip, 2)

print(f"Cuenta: ${bill}")
print(f"Propina: ${tip}")
print(f"Total: ${total}")

round() mantiene la salida con apariencia de dinero en lugar de una larga secuencia de decimales.

Contar páginas para paginación y rastrear el progreso como porcentaje:

python
total_items    = 153
items_per_page = 10

full_pages, leftover = divmod(total_items, items_per_page)
total_pages = (total_items + items_per_page - 1) // items_per_page

print(f"Páginas completas: {full_pages}, sobrantes: {leftover}")
print(f"Total de páginas necesarias: {total_pages}")   # 16
python
total_files     = 847
processed_files = 312

percent = round(processed_files / total_files * 100, 1)
print(f"Progreso: {processed_files}/{total_files} ({percent}%)")

La fórmula de división por techo (n + d - 1) // d es un truco estándar con enteros para redondear hacia arriba sin convertir a float.

Normalización min-max y cambio porcentual: dos patrones que aparecen constantemente en trabajo con datos:

python
# normalización min-max: escalar un valor al rango 0.0 a 1.0
value   = 75
minimum = 0
maximum = 100

normalised = (value - minimum) / (maximum - minimum)
print(f"Normalizado: {normalised:.2f}")   # 0.75

# cambio porcentual entre dos mediciones
before = 1_200
after  = 1_380

change = (after - before) / before * 100
print(f"Cambio: {change:.1f}%")          # 15.0%

Ambos patrones se reducen a una razón: un valor relativo a un rango de referencia o a una magnitud de referencia. La precisión de float es suficiente para la mayoría del trabajo analítico; el error acumulado solo importa cuando el cálculo encadena docenas de operaciones o involucra valores que difieren por muchos órdenes de magnitud.