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

Números y operaciones aritméticas

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

Los operadores aritméticos de Python cubren el conjunto estándar más división entera, módulo y exponenciación. Algunos comportamientos difieren de otros lenguajes de 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 del módulo verdadero.

Los enteros de Python no tienen overflow (el envolvimiento o error que encuentras en lenguajes con enteros de tamaño fijo): crecen tanto como lo permite la memoria. Los floats son los decimales de doble precisión habituales, bien para la mayoría del trabajo pero nunca exactos para dinero. Los operadores aritméticos siguen definiciones matemáticas en lugar de la convención de C que muchos lenguajes copian: // es división entera (redondea hacia el infinito negativo) y % lleva el signo del divisor, no del dividendo. Ambas opciones mantienen el mismo resultado sin importar si tus entradas son positivas o negativas, que es el comportamiento que quieres para ciclos y envolvimientos.

Los operadores

Los cuatro operadores de las matemáticas (+, -, *, /) funcionan exactamente como esperarías. Python suma 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 extienden lo que puedes expresar sin ningún trabajo extra.

Cada operador está conectado a un método dunder (un método nombrado con dobles guiones bajos que Python llama detrás de escenas): + a __add__, // a __floordiv__, % a __mod__, ** a __pow__, y así. La ganancia práctica es que tus propias clases pueden definir esos métodos y luego funcionar con + o * directamente, de la misma forma que un número integrado. Mezclar un int y un float en una operación siempre da un float, y / devuelve un float sin importar qué le pases.

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
JunoLos operadores+, -, *, / se comportan como las matemáticas en papel. Los tres extras valen la pena aprender desde temprano: // divide hacia un número entero, % te da el residuo, y ** eleva a una potencia. Uso % mucho más de lo que esperaba.
JunoLos operadores Los cuatro básicos funcionan como se espera, con una regla que guardar: / siempre devuelve un float. Los extras son // para división entera, % para el residuo, y ** para exponenciación. Cada uno te ahorra escribir un auxiliar.
JunoLos operadores Cada operador se enruta a un dunder, así que una clase que define __add__ o __mul__ se conecta directamente a + y *. Mezclar int y float siempre se expande a float, y / devuelve un float sin importar qué le des.

División: / vs //

/ siempre te da el resultado decimal exacto, incluso si la respuesta es un número entero. // suelta la parte fraccionaria y redondea hacia el infinito negativo. Para números positivos significa cortar el decimal, pero para negativos da un paso más lejos de cero:

/ siempre devuelve un float, sin importar si los operandos son enteros. // 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 truncación. Para negativos, no lo es:

/ es división verdadera y siempre devuelve un float. // es división entera: redondea el cociente verdadero hacia el infinito negativo en lugar de cortar hacia cero, que es lo que hacen muchos otros lenguajes. La razón por la que importa en la práctica: el // y % de Python satisfacen a == (a // b) * b + (a % b) para cada entrada entera, negativos incluidos. La división truncada rompe esa identidad para negativos, así que cualquier lógica de ciclo o envolvimiento que escribas se mantiene correcta aquí sin tener que considerar el signo especialmente.

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 infinito negativo, no hacia cero)

Usarás // principalmente con números positivos. Ten presente el comportamiento con negativos para cuando aparezcan.

JunoDivisión: / vs /// siempre devuelve un decimal, incluso cuando divide exactamente: 4 / 2 es 2.0. // suelta la parte fraccionaria, pero cuida los negativos: -7 // 2 es -4, no -3, porque redondea en lugar de cortar. Me atrapó la primera vez que lo hice.

Python llama esto división entera porque aplica la función piso matemática. Otros lenguajes cortan hacia cero en su lugar, dando un resultado diferente para negativos. El nombre // es una pista: divide, luego aplica piso.

JunoDivisión: / vs /// siempre devuelve un float, incluso 4 / 2 da 2.0. // redondea hacia piso en lugar de truncar, así que -7 // 2 es -4. Para números positivos los dos se ven iguales, la brecha solo se abre en negativos.

// calcula floor(a / b), no truncación. También funciona con floats: 7.5 // 2 es 3.0, el cociente redondeado devuelto como float en lugar de int.

JunoDivisión: / vs //// redondea el cociente, así que satisface a == (a // b) * b + (a % b) en cada entrada, negativos y todo. Esa es la propiedad en la que apoyarse para lógica de ciclos. Ten en cuenta que un operando float hace que // devuelva un float, como 7.5 // 2 dando 3.0.

El operador 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 módulo. Verificar par/impar es el uso más común, pero se generaliza a cualquier problema de ciclo o envolvimiento: 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. Muchos otros lenguajes toman el signo del dividendo en su lugar, así que darían -1 donde Python da 2 para -7 % 3. La respuesta de Python surge de definir módulo como a - (a // b) * b con // redondeando hacia el infinito negativo. La ganancia es que value % limit se mantiene dentro de 0 a limit - 1 incluso cuando value es negativo, así que un índice que se envuelve alrededor del final de una lista queda en rango sin una cláusula de protección.

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

6 % 2     # 0  (par)
7 % 2     # 1  (impar)
JunoEl operador residuo %% es lo que queda después de la división entera. 10 % 3 es 1 porque 3 cabe en 10 tres veces con 1 para sobra. Principalmente lo encontrarás como la verificación par/impar: n % 2 es 0 para par, 1 para impar.
JunoEl operador residuo %% es módulo, y va más allá de par/impar: cualquier vez que necesites mantener un valor dentro de un rango, value % limit lo envuelve a 0 hasta limit - 1. Contadores, asignación de grupos, ciclos repetidos, todo la misma forma.
JunoEl operador residuo %% sigue el signo del divisor, así que -7 % 3 es 2, no -1. Eso es lo que permite que value % limit se mantenga en rango con entradas negativas, sin cláusula de protección para un índice que se envuelve.

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:

** eleva a una potencia, y la forma de potencia fraccionaria es útil: n ** 0.5 es una raíz cuadrada sin importar nada. Dos operandos int dan un int, cualquier operando float da un float. Una cosa que saber antes de alcanzarlo para trabajo grande o preciso: 9 ** 0.5 pasa por matemáticas float, así que puede aterrizar en 2.9999999999999996 para algunas entradas. Cuando la exactitud importa, math.isqrt() para raíces enteras y math.pow() son las herramientas que considerar, cubiertas en el capítulo Módulos.

python
2 ** 10    # 1024
3 ** 3     # 27
9 ** 0.5   # 3.0  (raíz cuadrada: eleva a la potencia de 0.5)
JunoExponenciación **** eleva un número a una potencia, así que 2 ** 10 es 1024. Una potencia fraccionaria te da una raíz: 9 ** 0.5 es 3.0. La única trampa es el símbolo, usa **, no ^, que hace algo no relacionado en Python.
JunoExponenciación **** es exponenciación, y toma floats, así que 9 ** 0.5 te da una raíz cuadrada sin llamada a función separada. Alcanza la forma de potencia fraccionaria cuando quieres una raíz rápida en línea.
JunoExponenciación ** Dos ints dan un int, cualquier float da un float. La trampa con el truco ** 0.5 es que se ejecuta a través de matemáticas float y puede desviarse, así que alcanza math.isqrt() o math.pow() cuando la exactitud cuenta.

Precedencia de operadores

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

Python sigue el orden PEMDAS/BODMAS estándar. La parte que atrapa 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 cuando el orden no sea claro de una mirada:

Precedencia de mayor a menor entre operadores aritméticos: **, luego menos unario (un signo menos en frente de un valor único), luego * / // % (izquierda-a-derecha cuando comparten un nivel), luego + -. La única trampa que atrapa en código real: -2 ** 2 es -(2 ** 2), que es -4, porque ** se une más fuerte que el menos inicial. Si alguna vez elevas al cuadrado un valor que podría ser negativo, pon paréntesis como (-2) ** 2 o el signo estará mal sin ningún error que te advierta.

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
JunoPrecedencia de operadores Mismo orden que las matemáticas en papel: potencias primero, luego multiplicar y dividir, luego sumar y restar. Cuando el orden no sea claro de una mirada, añade paréntesis. No cuestan nada y expresan exactamente lo que quisiste.
JunoPrecedencia de operadores Orden PEMDAS estándar, con una trampa: /, // y % se sientan en el mismo nivel y corren de izquierda a derecha cuando se mezclan. Esa regla de izquierda-a-derecha es la parte que la gente malinterpreta, así que pon paréntesis cuando el agrupamiento no sea claro.
JunoPrecedencia de operadores La trampa es el menos unario uniéndose más flojo que **, así que -2 ** 2 es -4. Eleva al cuadrado cualquier cosa que podría ser negativa como (-2) ** 2, o el signo se invierte sin error que lo marque.

Cómo interactúan int y float

Python tiene una regla consistente: / siempre devuelve un decimal, incluso 4 / 2 da 2.0. Cualquier operación que mezcle un entero y un decimal también da un decimal. Cuando necesites 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, que importa cuando necesitas un entero (por ejemplo, para usar como índice).

La regla es fija y vale la pena interiorizar: int se expande a float siempre que los dos se mezclen, / siempre devuelve un float, y // devuelve un int solo cuando ambos operandos son int. El error que esto previene es un float deslizándose en algo que necesita un número entero, como un índice de lista o una clave de diccionario, donde 2.0 y 2 no son intercambiables. Cuando un cálculo podría producir un float y necesitas un int, convierte con int() en ese punto en lugar de esperar que la división salga exacta.

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)
JunoCómo interactúan int y float/ siempre da un decimal, así que 4 / 2 es 2.0, no 2. Mezcla un número entero con un decimal en cualquier lugar y obtienes un decimal. Cuando necesitas un número entero, alcanza // o envuélvelo en int().
JunoCómo interactúan int y float/ es siempre float, // y % en dos ints se mantienen int, y cualquier int mezclado con un float sale float. El que atrapa a la gente es 4 / 2 siendo 2.0 cuando querías un índice.
JunoCómo interactúan int y float Un float errante de / rompe cualquier cosa que necesite un int exacto, como un índice o una clave de dict donde 2.0 no es 2. Convierte con int() en ese límite en lugar de confiar en que la división salga exacta.

Precisión de float

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

python
0.1 + 0.2   # 0.30000000000000004

Ese error minúsculo no es un error de Python. Las computadoras almacenan números decimales en binario, y algunos valores como 0.1 no pueden representarse exactamente. Es similar a cómo 1/3 no puede escribirse 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á el resultado ordenado.

Los floats te dan aproximadamente 15 a 16 dígitos decimales significativos de precisión. La imprecisión aparece porque algunas fracciones no pueden almacenarse exactamente en binario, así que 0.1 + 0.2 produce 0.30000000000000004. La desviación solo se muestra cuando inspecciones el valor bruto; formatear con :.2f o round() la oculta en la salida.

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

Un float almacena números en binario (base 2), y cualquier fracción cuyo denominador no sea una potencia de dos (como 1/10) no tiene forma binaria exacta, de la misma manera que 1/3 no tiene forma decimal exacta. Esa es la razón completa por la que 0.1 + 0.2 aterriza en 0.30000000000000004. El error por operación es minúsculo, pero se acumula a través de una larga cadena de aritmética, así que dos formas de calcular el mismo total pueden no coincidir en los últimos dígitos.

La regla que previene el error: nunca uses un float para dinero, y nunca prueba floats para igualdad exacta con ==. Alcanza decimal.Decimal cuando necesites aritmética exacta en base 10 (facturación, contabilidad), o fractions.Fraction cuando necesites razones exactas sin ningún redondeo en absoluto. Ambas vienen en la biblioteca estándar de Python, cubiertas en el capítulo Módulos.

JunoPrecisión de float Las computadoras almacenan decimales en binario, y algunos valores como 0.1 no tienen forma binaria exacta, así que 0.1 + 0.2 sale como 0.30000000000000004. No es un error de Python, cada lenguaje hace esto. Para salida cotidiana, round() o :.2f lo mantiene ordenado.
JunoPrecisión de float Binario no puede sostener cada decimal exactamente, así que 0.1 + 0.2 se desvía a 0.30000000000000004. Bien para la mayoría de matemáticas, y :.2f o round() ordena la pantalla. Cuando los centavos tienen que sumar, cambia a decimal.Decimal.
JunoPrecisión de float La regla que te salva: sin floats para dinero, y nunca == en floats, ya que el error se acumula a través de una cadena de operaciones. Usa decimal.Decimal para base 10 exacta y fractions.Fraction para razones exactas.

Literales numéricos legibles

Python te permite poner guiones bajos en literales numéricos para hacer números grandes más legibles. Python los ignora completamente; están ahí para ti:

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

Los guiones bajos se quitan antes de que Python vea el valor, así que tienen cero efecto en él. Funcionan en enteros, floats y literales basados como se parecen (0xFF_FF, 0b1010_0001, 1_234.567_890), lo que los hace útiles para agrupar bytes en una constante hex, no solo miles en uno decimal. Las únicas restricciones: un guión bajo no puede sentarse al inicio, al final, o al lado de un punto decimal o marcador de exponente.

python
population = 8_100_000_000
distance_km = 384_400
pi_approx = 3.141_592_653
JunoLiterales numéricos legibles Mete guiones bajos en un número largo para hacerlo legible: 8_100_000_000 es el mismo valor que 8100000000. Python los ignora completamente, están ahí para tus ojos, no para el intérprete.
JunoLiterales numéricos legibles Los guiones bajos son válidos en cualquier lugar dentro de un literal numérico y desaparecen al analizar, así que el valor no cambia. Bueno para separadores de miles en constantes, e igualmente útil para agrupar dígitos en binario o hex.
JunoLiterales numéricos legibles Los guiones bajos son cosméticos en enteros, floats y literales basados, así que 0xFF_FF y 0b1010_0001 se leen más limpio. Las únicas reglas: no al inicio, no al final, ni al lado de un punto decimal o marcador de exponente.

Funciones integradas útiles

abs()

abs() devuelve el valor absoluto: siempre positivo, sin importar el signo de la entrada. Úsalo cuando te importe a qué distancia está un número de cero, no en qué dirección.

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

abs() devuelve la magnitud de un número, con el tipo de retorno igualando la entrada: un int adentro da un int afuera, un float da un float. El uso cotidiano es comparar a qué distancia se sientan dos valores de un objetivo sin importar qué lado caen, por ejemplo abs(measured - expected) < tolerance como una verificación de tolerancia, que lee más claramente que dos comparaciones contra un rango con signo.

python
abs(-5)     # 5
abs(3.7)    # 3.7
abs(-0.5)   # 0.5
Junoabs()abs() quita el signo y devuelve el tamaño positivo: abs(-5) es 5, abs(3.7) es 3.7. Alcanza cuando te importe a qué distancia está un número de cero, no en qué dirección se inclina.
Junoabs()abs() da la magnitud de un int o float, dirección descartada. Es la forma limpia de escribir distancia y verificaciones de margen de error, en cualquier lugar donde el signo no sea parte de la pregunta.
Junoabs()abs() mantiene el tipo de entrada, un int se mantiene int. El patrón que vale la pena guardar es abs(measured - expected) < tolerance: una verificación de tolerancia en lugar de dos comparaciones alrededor de un rango con signo.

round()

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

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

Una cosa 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 mitad de camino entre dos opciones.

round() usa redondeo de banquero: cuando el valor está exactamente a mitad de camino, redondea al número par más cercano en lugar de siempre redondearse 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() usa redondeo-mitad-a-par (redondeo de banquero): un empate exacto va al entero par más cercano, no siempre hacia arriba. Eso mantiene los errores de redondeo de sesgar una columna larga de figuras en una dirección, por eso es el predeterminado para trabajo estadístico. Dos precauciones prácticas. Primero, un empate float a menudo no es un empate exacto una vez que está en binario, así que round(2.675, 2) da 2.67, no 2.68, porque 2.675 no puede almacenarse exactamente. Segundo, cuando los centavos tienen que redondear de forma predecible, hazlo con decimal.Decimal y un modo de redondeo explícito en lugar de confiar en round() sobre floats.

python
round(2.5)   # 2
round(3.5)   # 4
round(4.5)   # 4
Junoround()round(x) va al número entero más cercano, round(x, n) mantiene n lugares decimales. La sorpresa: un valor sentado exactamente a mitad de camino redondea al número par más cercano, así que round(2.5) es 2, no 3.
Junoround()round() usa redondeo de banquero, así que empates exactos van al número par más cercano: round(2.5) es 2, round(4.5) es 4. Corta sesgo en trabajo agregado, pero espéralo cuando asumiste que los empates siempre redondean hacia arriba.
Junoround() Redondeo de banquero más binario significa round(2.675, 2) es 2.67, ya que 2.675 no es un empate exacto bajo el capó. Para moneda, redondea con decimal.Decimal y un modo elegido en lugar de confiar en round() sobre floats.

divmod()

divmod() te da tanto el cociente como el residuo en una sola llamada. Devuelve un par de valores, una tupla (cubierta en el capítulo Tuplas y conjuntos), 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. Úsalo cuando necesites ambos valores de todas formas: paginación, conversión de tiempo, o distribuir elementos en grupos.

divmod(a, b) hace la división una vez y devuelve tanto el cociente de piso como el residuo módulo como un par, así evitas recalcular la misma división a través de // y luego %. Se lee más limpio en los lugares donde necesitas ambas mitades de todas formas: conversión de tiempo (divmod(seconds, 60)), paginación, o distribuir elementos a lo largo de filas de tamaño fijo.

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
Junodivmod()divmod(a, b) devuelve el cociente y el residuo juntos: divmod(10, 3) es (3, 1). Puedes desempacar ambos a la vez con quotient, remainder = divmod(10, 3).
Junodivmod()divmod(a, b) es (a // b, a % b) en un paso. Alcanza cuando necesites ambas piezas, como conversión de tiempo o paginación, en lugar de calcular la división dos veces.
Junodivmod() Una división, ambos resultados: divmod() salta el trabajo redundante de // y % separados. Se lee mejor donde quieres ambas mitades de todas formas, como divmod(seconds, 60) o disposición de filas.

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"Bill:  ${bill}")
print(f"Tip:   ${tip}")
print(f"Total: ${total}")

round() mantiene la salida pareciendo dinero en lugar de una larga corrida de lugares decimales.

Contar páginas para paginación y rastrear 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"Full pages: {full_pages}, leftover: {leftover}")
print(f"Total pages needed: {total_pages}")   # 16
python
total_files = 847
processed_files = 312

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

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

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

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

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

# cambio de porcentaje entre dos mediciones
before = 1_200
after = 1_380

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

Ambos patrones se reducen a una razón: un valor relativo a un rango de referencia o 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.