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

Números y aritmética

docs.scrimba.com

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

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 de piso redondea hacia el infinito negativo, y el módulo sigue la semántica del módulo verdadero.

Los enteros de Python no tienen desbordamiento (el envolvimiento o error que alcanzas en lenguajes con enteros de tamaño fijo): crecen tanto como la memoria lo permita. Los floats son los decimales de doble precisión usuales, 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 de piso (redondea hacia el infinito negativo) y % lleva el signo del divisor, no del dividendo. Ambas opciones mantienen el mismo resultado ya sean tus entradas 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 agrega 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 guiones bajos dobles que Python llama detrás de escenas): + a __add__, // a __floordiv__, % a __mod__, ** a __pow__, y así sucesivamente. La recompensa práctica es que tus propias clases pueden definir esos métodos y luego trabajar 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 alimentes.

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 de papel. Los tres extras valen la pena aprender pronto: // divide hacia un número entero, % te da el residuo, y ** eleva a una potencia. Uso % más de lo que esperaba.
JunoLos operadores Los cuatro básicos funcionan como se espera, con una regla a guardar: / siempre devuelve un float. Los extras son // para división entera, % para el residuo, y ** para exponenciación. Cada uno te evita escribir un asistente.
JunoLos operadores Cada operador se direcciona a un dunder, así que una clase que define __add__ o __mul__ se conecta directamente a + y *. Mezclar int y float siempre se amplía a float, y / devuelve un float independientemente de lo que 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, independientemente de si los entrados son enteros. // devuelve el piso del resultado: el entero más grande que es menor o igual que el resultado verdadero. Para números positivos esto es lo mismo que truncamiento. Para negativos, no lo es:

/ es división verdadera y siempre devuelve un float. // es división de piso: redondea el cociente verdadero hacia el infinito negativo en lugar de cortarlo 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, incluidos los negativos. La división de truncamiento rompe esa identidad para negativos, así que cualquier lógica de ciclo o envolvimiento que escribas se mantiene correcta aquí sin casos especiales para el signo.

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

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

Usarás principalmente // con números positivos. Mantén el comportamiento negativo en mente para cuando aparezcan.

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

Python llama a esto división de piso 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 piso.

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

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

JunoDivisión: / vs //// redondea el cociente, así que satisface a == (a // b) * b + (a % b) en cada entrada, negativos incluidos. Esa es la propiedad en la que puedes apoyarte 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 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 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 recompensa es que value % limit se mantiene dentro de 0 a limit - 1 incluso cuando value va negativo, así que un índice que se envuelve alrededor del final de una lista cae en rango sin una cláusula de protección.

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

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

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 deja expresar raíces como potencias fraccionarias en lugar de una llamada de 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 a saber antes de que la alcances en trabajo grande o preciso: 9 ** 0.5 va a través de matemáticas float, así que puede caer en 2.9999999999999996 para algunas entradas. Cuando la exactitud importa, math.isqrt() para raíces enteras y math.pow() son las herramientas a evaluar, cubiertas en el capítulo Módulos.

python
2 ** 10    # 1024
3 ** 3     # 27
9 ** 0.5   # 3.0  (raíz cuadrada: elevar 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 de función separada. Alcanza la forma de potencia fraccionaria cuando quieras 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 de ** 0.5 raíz es que va a través de matemáticas float y puede derivarse, así que alcanza math.isqrt() o math.pow() cuando la exactitud cuenta.

Precedencia de operadores

Python sigue el orden estándar de matemáticas: 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 estándar PEMDAS/BODMAS. La parte que atrapa a la gente: /, //, y % comparten todos 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 claro de una mirada:

Precedencia de más alto a más bajo entre operadores aritméticos: **, luego - unario (un signo menos frente a un valor simple), luego * / // % (izquierda a derecha cuando comparten un nivel), luego + -. La única trampa que pica 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 un valor que podría ser negativo, entre paréntesis como (-2) ** 2 o el signo será incorrecto sin error que lo 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 matemáticas de papel: potencias primero, luego multiplicar y dividir, luego sumar y restar. Cuando el orden no es claro de una mirada, agrega paréntesis. No cuestan nada y deletrean exactamente lo que quisiste decir.
JunoPrecedencia de operadores Orden estándar PEMDAS, con una complicación: /, // y % todos se sientan al mismo nivel y se ejecutan de izquierda a derecha cuando se mezclan. Esa regla de izquierda a derecha es la parte que la gente malinterpreta, así que entre paréntesis siempre que la agrupación no sea clara.
JunoPrecedencia de operadores La trampa es que el menos unario se une menos fuerte que **, así que -2 ** 2 es -4. Eleva cualquier cosa que podría ser negativa como (-2) ** 2, o el signo se voltea 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 un índice).

La regla es fija y vale la pena internalizarla: int se amplía a float siempre que los dos se mezclen, / siempre devuelve un float, y // devuelve un int solo cuando ambos operandos son int. El bug que esto previene es un float colá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 resultara entera.

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 necesites 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 quedan 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` extraviado de / rompe cualquier cosa que necesite un `int` exacto, como un índice o una clave dict donde 2.0 no es 2. Convierte con int() en ese límite en lugar de confiar en que la división salga entera.

Precisión de float

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 números decimales en binario, y algunos valores como 0.1 no pueden ser representados exactamente. Es similar a cómo 1/3 no puede ser escrito 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á la salida ordenada.

Los floats te dan aproximadamente 15 a 16 dígitos decimales significativos de precisión. La imprecisión emerge porque algunas fracciones no pueden ser almacenadas exactamente en binario, así que 0.1 + 0.2 produce 0.30000000000000004. La derivación solo se muestra cuando inspecciona el valor bruto; formatear con :.2f o round() la oculta en 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. Eso está cubierto 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 forma que 1/3 no tiene forma decimal exacta. Esa es toda la razón por la que 0.1 + 0.2 cae en 0.30000000000000004. El error por operación es diminuto, pero se acumula en una larga cadena de aritmética, así que dos formas de calcular el mismo total pueden discrepar en los últimos dígitos.

La regla que previene el bug: nunca uses un float para dinero, y nunca pruebas floats para igualdad exacta con ==. Alcanza decimal.Decimal cuando necesites aritmética exacta en base 10 (facturación, contabilidad), o fractions.Fraction cuando necesites cocientes exactos sin ningún redondeo. Ambos se incluyen 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 bug de Python, cada lenguaje hace esto. Para salida cotidiana, round() o :.2f lo mantiene ordenado.
JunoPrecisión de float El binario no puede mantener cada decimal exactamente, así que 0.1 + 0.2 deriva a 0.30000000000000004. Bien para la mayoría de matemáticas, y :.2f o round() ordenan la pantalla. Cuando los centavos tienen que sumarse, cambia a decimal.Decimal.
JunoPrecisión de float La regla que te salva: no floats para dinero, y nunca == en floats, ya que el error se acumula en una cadena de operaciones. Usa decimal.Decimal para exacto en base 10 y fractions.Fraction para cocientes exactos.

Literales numéricos legibles

Python te deja 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 eliminan 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 eliminan antes de que Python vea el valor, así que no tienen efecto cero en él. Funcionan en enteros, floats y literales basados igualmente (0xFF_FF, 0b1010_0001, 1_234.567_890), lo que los hace útiles para agrupar bytes en una constante hex, no solo miles en una 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 Suelta 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 en tiempo de análisis, así que el valor no cambia. Bien para separadores de miles en constantes, e igualmente útil para agrupar dígitos en hexadecimal o binario.
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 limpios. Las únicas reglas: no al inicio, al final, o al lado de un punto decimal o marcador de exponente.

Funciones integradas útiles

abs()

abs() devuelve el valor absoluto: siempre positivo, independientemente del signo de la entrada. Úsalo cuando te importe qué tan lejos un número está de cero, no 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 coincidiendo con la entrada: un int adentro da un int afuera, un float da un float. El uso cotidiano es comparar qué tan lejos dos valores se sientan de un objetivo sin importar cuál lado caen, por ejemplo abs(measured - expected) < tolerance como una verificación de tolerancia, que se lee más claramente que dos comparaciones contra un rango firmado.

python
abs(-5)     # 5
abs(3.7)    # 3.7
abs(-0.5)   # 0.5
Junoabs()abs() elimina el signo y devuelve el tamaño positivo: abs(-5) es 5, abs(3.7) es 3.7. Alcánzalo cuando te importe qué tan lejos un número está de cero, no 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 queda un `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 firmado.

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

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 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() usa redondeo-mitad-a-par (redondeo de banquero): un empate exacto va al entero par más cercano, no siempre hacia arriba. Eso mantiene errores de redondeo de inclinar una larga columna de figuras en una dirección, que es por qué es el predeterminado para trabajo estadístico. Dos advertencias 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 ser almacenado exactamente. Segundo, cuando los centavos tienen que sumar, 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 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 el sesgo en trabajo agregado, pero espéralo cuando asumiste que las mitades siempre redondean hacia arriba.
Junoround() Redondeo de banquero más binario significa que round(2.675, 2) es 2.67, ya que 2.675 no es un empate exacto bajo el capó. Para dinero, 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 todos modos: paginación, conversión de tiempo, o distribución de 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í que evitas recomputar la misma división a través de // y luego %. Se lee más limpio en los lugares donde necesitas ambas mitades de todos modos: conversión de tiempo (divmod(seconds, 60)), paginación, o distribución de elementos en 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. Alcánzalo 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() omite el trabajo redundante de `//` y `%` separados. Se lee mejor donde quieres ambas mitades de todos modos, como divmod(seconds, 60) o distribució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 carrera de decimales.

Contando páginas para paginación y rastreando progreso como un 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 de enteros 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 de datos:

python
# normalización min-max: escala un valor en el 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.