Redondear parece fácil hasta que deja de serlo
Pocas tareas de programación parecen tan inocentes como redondear un número. Un precio de 19,9876 debe mostrarse como 19,99; una media de 7,456 debe aparecer como 7,46; un porcentaje calculado con quince decimales necesita quedarse en dos. Python ofrece una función integrada para ello y en la mayoría de los casos funciona exactamente como se espera.
El problema aparece en los bordes. Un día alguien descubre que round(2.675, 2) devuelve 2.67 en lugar de 2.68, o que round(2.5) da 2 y no 3, y empieza a sospechar que Python tiene un error. No lo tiene: ambos comportamientos tienen una explicación precisa que todo desarrollador debería conocer, sobre todo si trabaja con dinero, informes financieros o datos científicos.
En este artículo vamos a recorrer todas las formas de redondear a dos decimales en Python, desde la función round() hasta el módulo decimal, pasando por las f-strings y las funciones del módulo math. Veremos cuándo conviene cada una, por qué los números de punto flotante producen resultados sorprendentes y cómo evitar los errores más habituales en aplicaciones reales. Todos los ejemplos funcionan con Python 3.15, la versión estable más reciente, y con cualquier versión de Python 3 aún soportada.
Qué significa redondear a un número de decimales
Antes de escribir código conviene repasar el concepto. En un número como 482.736, cada dígito ocupa una posición con un valor distinto. A la izquierda del punto están las unidades, decenas y centenas; a la derecha, las décimas, centésimas y milésimas:
numero = 482.736
# 4 → centenas
# 8 → decenas
# 2 → unidades
# 7 → décimas
# 3 → centésimas
# 6 → milésimas
Redondear a dos decimales significa conservar hasta las centésimas y decidir qué hacer con el resto. La regla escolar es sencilla: se mira el primer dígito que se va a descartar. Si es 5 o mayor, el último dígito conservado aumenta en una unidad; si es menor que 5, se queda igual.
Aplicando esa regla a nuestro ejemplo, el dígito que se descarta es el 6, que es mayor que 5, así que el 3 de las centésimas pasa a ser 4. El resultado es 482.74. Con 482.732, el dígito descartado sería un 2 y el resultado quedaría en 482.73.
Esta regla, conocida como redondeo hacia arriba en el punto medio, es la que aprendemos en la escuela. Como veremos enseguida, no es la única que existe ni la que Python usa por defecto.
La función round() en Python
La herramienta más directa es la función integrada round(). Recibe el número y, opcionalmente, la cantidad de decimales que queremos conservar:
temperatura = 23.4871
precio = 149.9549
distancia = 7.0
print(round(temperatura, 2))
print(round(precio, 2))
print(round(distancia, 2))
23.49
149.95
7.0
El segundo argumento, llamado ndigits, admite varios usos que conviene conocer:
valor = 1234.5678
print(round(valor)) # sin segundo argumento: entero
print(round(valor, 0)) # cero decimales: float
print(round(valor, 1)) # una décima
print(round(valor, -2)) # negativo: redondea a centenas
1235
1235.0
1234.6
1200.0
Hay un detalle sobre el tipo de retorno que causa confusiones. Si se omite ndigits, round() devuelve un entero de tipo int. Si se indica, aunque sea cero, devuelve un número del mismo tipo que el original, en este caso un float. Esto importa cuando el resultado se usa como índice de una lista o en una operación que exige enteros.
El tercer punto a tener en cuenta es que round() no añade ceros a la derecha. round(7.0, 2) devuelve 7.0, no 7.00, porque para Python ambos son el mismo número. Mostrar dos decimales fijos es una cuestión de formato, no de redondeo, y lo veremos en una sección propia.
El redondeo del banquero: por qué round(2.5) devuelve 2
Prueba el siguiente código y probablemente te sorprenda:
for valor in [0.5, 1.5, 2.5, 3.5, 4.5, -2.5]:
print(valor, "→", round(valor))
0.5 → 0
1.5 → 2
2.5 → 2
3.5 → 4
4.5 → 4
-2.5 → -2
Cuando un número está exactamente a mitad de camino entre dos opciones, Python no siempre redondea hacia arriba. Elige el resultado par más cercano. Esta estrategia se llama redondeo al par, y popularmente se conoce como redondeo del banquero en Python y en muchos otros sistemas.
La razón es estadística. Si siempre redondeamos los valores medios hacia arriba, al sumar miles de números redondeados el total tenderá a crecer más de lo debido, porque todos los empates empujan en la misma dirección. Al alternar entre arriba y abajo según la paridad, los errores se compensan y la suma se mantiene más fiel a la realidad. Por eso es el modo de redondeo predeterminado del estándar IEEE 754 que siguen los procesadores modernos, y el que adoptó Python a partir de la versión 3.
El efecto se ve claramente con un experimento:
valores = [x + 0.5 for x in range(1000)] # 0.5, 1.5, 2.5, ...
suma_real = sum(valores)
suma_al_par = sum(round(v) for v in valores)
suma_hacia_arriba = sum(int(v + 0.5) for v in valores)
print(suma_real, suma_al_par, suma_hacia_arriba)
500000.0 500000 500500
El redondeo al par conserva exactamente la suma original, mientras que el redondeo escolar acumula un sesgo de 500 unidades.
En la práctica, este comportamiento solo afecta a los empates exactos. Un valor como 2.51 siempre se redondea a 3, y 2.49 siempre a 2. Pero si tu aplicación tiene requisitos legales o contables que exigen el redondeo escolar, necesitarás el módulo decimal, que trataremos más adelante.
El problema de la representación en punto flotante
Ahora el caso que más dudas genera:
print(round(2.675, 2))
print(round(1.005, 2))
print(round(0.285, 2))
2.67
1.0
0.28
Según el redondeo al par, 2.675 debería convertirse en 2.68, porque el 8 es par. Sin embargo, Python devuelve 2.67. La explicación no tiene nada que ver con la función round(), sino con cómo se almacenan los números decimales en memoria.
Los números de tipo float se guardan en binario con 64 bits, siguiendo el estándar IEEE 754. Igual que en base decimal no podemos escribir un tercio con un número finito de dígitos, en base binaria la mayoría de las fracciones decimales tampoco tienen una representación exacta. El número 2.675 que escribimos en el código se convierte en el valor binario más cercano disponible, que es ligeramente menor. Podemos ver ese valor real con el módulo decimal:
from decimal import Decimal
print(Decimal(2.675))
print(Decimal(0.1))
2.67499999999999982236431605997495353221893310546875
0.1000000000000000055511151231257827021181583404541015625
Como el valor almacenado es 2.67499999..., no hay empate: el dígito que se descarta es un 4 y el resultado correcto es 2.67. Python no se equivoca; redondea con precisión el número que realmente tiene en memoria.
Estos errores de punto flotante aparecen también en operaciones aritméticas simples:
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
total = 0.0
for _ in range(10):
total += 0.1
print(total)
0.30000000000000004
False
0.9999999999999999
Para comparar números de punto flotante nunca debe usarse el operador de igualdad directamente. El módulo math ofrece una función pensada para ello:
import math
print(math.isclose(0.1 + 0.2, 0.3))
print(math.isclose(total, 1.0))
True
True
Cabe mencionar que la función integrada sum() aplica, desde Python 3.12, un algoritmo de suma compensada que reduce estos errores al sumar muchos valores de punto flotante. Aun así, la representación de cada número individual sigue siendo inexacta, y para cálculos que exigen exactitud decimal la herramienta adecuada es otra.
Mostrar dos decimales con f-strings y format
Muchas veces no necesitamos cambiar el valor de un número, sino solo presentarlo con dos decimales en una interfaz, un informe o un archivo. Para eso, el formato de cadenas es la opción correcta, y el formato de números con f-strings es la forma más legible de hacerlo:
precio = 49.5
impuesto = 3.14159
total = precio + impuesto
print(f"Precio: {precio:.2f}")
print(f"Impuesto: {impuesto:.2f}")
print(f"Total: {total:.2f}")
Precio: 49.50
Impuesto: 3.14
Total: 52.64
El especificador .2f indica un número de punto fijo con exactamente dos decimales. A diferencia de round(), siempre muestra los ceros a la derecha, que es justo lo que se espera al presentar importes.
El mismo especificador funciona con el método format() y con el operador %, que encontrarás en código más antiguo:
valor = 3.14159
print(f"{valor:.2f}")
print("{:.2f}".format(valor))
print(format(valor, ".2f"))
print("%.2f" % valor)
3.14
3.14
3.14
3.14
El mini lenguaje de formato permite combinar los decimales con otras opciones muy útiles:
ventas = 1234567.891
crecimiento = 0.04567
saldo = -85.2
print(f"{ventas:,.2f}") # separador de miles
print(f"{crecimiento:.1%}") # porcentaje con un decimal
print(f"{saldo:+.2f}") # signo siempre visible
print(f"{ventas:>15,.2f}|") # alineado a la derecha en 15 caracteres
print(f"{3.5:08.2f}") # relleno con ceros
1,234,567.89
4.6%
-85.20
1,234,567.89|
00003.50
Un detalle importante: el formato de cadenas aplica el mismo redondeo que round(), sobre el valor binario real. Por eso f"{2.675:.2f}" muestra 2.67. Además, el resultado es una cadena de texto, no un número, así que no debe usarse para seguir calculando.
Si necesitas el formato propio de un país, con coma decimal y punto para los miles, el módulo locale permite adaptarlo, aunque depende de que la configuración regional esté instalada en el sistema:
import locale
locale.setlocale(locale.LC_ALL, "es_ES.UTF-8")
print(locale.format_string("%.2f", 1234567.891, grouping=True))
1.234.567,89
El módulo decimal para cálculos exactos
Cuando la exactitud decimal es imprescindible, como en facturación, contabilidad o cálculo de impuestos, el módulo decimal de Python es la herramienta adecuada. Su tipo Decimal almacena los números en base diez, de modo que 0.1 es exactamente una décima, sin aproximaciones binarias:
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
0.3
True
Hay una regla de oro al crear valores Decimal: hazlo siempre a partir de cadenas de texto o enteros, nunca a partir de float. Si pasas un float, el Decimal hereda exactamente la imprecisión binaria que querías evitar, como vimos antes con Decimal(2.675).
Para redondear a dos decimales se usa el método quantize(), que ajusta el número al exponente de otro valor de referencia. El segundo argumento indica el modo de redondeo:
from decimal import Decimal, ROUND_HALF_UP
importe = Decimal("2.675")
centimos = Decimal("0.01")
print(importe.quantize(centimos, rounding=ROUND_HALF_UP))
2.68
Ahora sí obtenemos el resultado que esperaría un contable. El valor es exacto y el modo ROUND_HALF_UP aplica la regla escolar de redondear los empates hacia arriba.
El módulo ofrece varios modos de redondeo para cubrir distintas normativas:
from decimal import (Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN,
ROUND_HALF_DOWN, ROUND_UP, ROUND_DOWN,
ROUND_CEILING, ROUND_FLOOR)
valor = Decimal("0.125")
modos = [ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_HALF_DOWN,
ROUND_UP, ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR]
for modo in modos:
print(f"{modo:<16} {valor.quantize(Decimal('0.01'), rounding=modo)}")
ROUND_HALF_UP 0.13
ROUND_HALF_EVEN 0.12
ROUND_HALF_DOWN 0.12
ROUND_UP 0.13
ROUND_DOWN 0.12
ROUND_CEILING 0.13
ROUND_FLOOR 0.12
Los modos HALF solo actúan diferente en los empates. ROUND_UP y ROUND_DOWN se alejan o se acercan siempre a cero, mientras que ROUND_CEILING y ROUND_FLOOR siempre suben o bajan hacia el infinito positivo o negativo.
También se puede aplicar round() directamente a un Decimal. En ese caso se usa el modo del contexto activo, que por defecto es el redondeo al par:
from decimal import Decimal
print(round(Decimal("2.675"), 2))
print(round(Decimal("2.665"), 2))
2.68
2.66
Contextos y precisión en Decimal
El comportamiento del módulo decimal se controla mediante contextos, que definen la precisión, el modo de redondeo y qué situaciones deben lanzar excepciones. Se puede modificar el contexto global o crear uno temporal para un bloque de código:
from decimal import Decimal, localcontext, ROUND_HALF_UP
with localcontext() as ctx:
ctx.rounding = ROUND_HALF_UP
print(round(Decimal("2.665"), 2))
print(round(Decimal("2.665"), 2)) # fuera del bloque vuelve al modo por defecto
2.67
2.66
Conviene no confundir la precisión del contexto con el número de decimales. La precisión indica la cantidad total de dígitos significativos de los resultados, no los decimales:
from decimal import Decimal, getcontext
getcontext().prec = 6
print(Decimal(1) / Decimal(7))
print(Decimal(1000) / Decimal(7))
0.142857
142.857
Por eso, para fijar dos decimales en importes monetarios, lo correcto es usar quantize() en el punto adecuado del cálculo, y no reducir la precisión global.
Un patrón muy práctico es centralizar el redondeo en una función auxiliar que toda la aplicación reutilice:
from decimal import Decimal, ROUND_HALF_UP
CENTIMOS = Decimal("0.01")
def a_dinero(valor) -> Decimal:
"""Convierte un valor a Decimal redondeado a céntimos."""
return Decimal(str(valor)).quantize(CENTIMOS, rounding=ROUND_HALF_UP)
print(a_dinero("19.995"))
print(a_dinero(7))
print(a_dinero(2.675))
20.00
7.00
2.68
Convertir el float a cadena con str() antes de crear el Decimal aprovecha que Python muestra la representación decimal más corta del número, lo que evita arrastrar la imprecisión binaria cuando el valor de entrada llega como float desde otra parte del sistema.
Truncar, redondear hacia arriba o hacia abajo con math
No siempre queremos el valor más cercano. A veces la lógica de negocio exige truncar los decimales sobrantes o redondear siempre en una dirección concreta. El módulo math ofrece tres funciones para ello, aunque trabajan con enteros, por lo que hay que escalar el número:
import math
def truncar(valor: float, decimales: int = 2) -> float:
factor = 10 ** decimales
return math.trunc(valor * factor) / factor
def hacia_arriba(valor: float, decimales: int = 2) -> float:
factor = 10 ** decimales
return math.ceil(valor * factor) / factor
def hacia_abajo(valor: float, decimales: int = 2) -> float:
factor = 10 ** decimales
return math.floor(valor * factor) / factor
print(truncar(3.789), truncar(-3.789))
print(hacia_arriba(3.781), hacia_abajo(3.789))
3.78 -3.78
3.79 3.78
La diferencia entre truncar y redondear hacia abajo solo se nota con números negativos: trunc se acerca a cero, mientras que floor va hacia el infinito negativo.
Un caso de uso típico de ceil es calcular cuántos paquetes o páginas se necesitan, donde cualquier fracción obliga a añadir una unidad completa. Uno de floor es calcular cuánto puede retirarse de un saldo sin superarlo.
Hay que advertir que este método hereda los problemas de punto flotante, porque la multiplicación por el factor puede producir valores como 378.99999999. Para resultados críticos, es más seguro usar Decimal con los modos ROUND_DOWN, ROUND_UP, ROUND_FLOOR o ROUND_CEILING:
from decimal import Decimal, ROUND_DOWN
print(Decimal("3.789").quantize(Decimal("0.01"), rounding=ROUND_DOWN))
3.78
Redondear colecciones de datos
En el análisis de datos es habitual redondear listas, diccionarios o tablas completas. Con las estructuras nativas de Python basta con una comprensión:
mediciones = [12.3456, 7.891, 0.005, 99.999]
ventas = {"enero": 15234.567, "febrero": 18901.234, "marzo": 17450.5}
print([round(m, 2) for m in mediciones])
print({mes: round(v, 2) for mes, v in ventas.items()})
[12.35, 7.89, 0.01, 100.0]
{'enero': 15234.57, 'febrero': 18901.23, 'marzo': 17450.5}
Las librerías de análisis de datos ofrecen sus propias funciones vectorizadas, mucho más rápidas para grandes volúmenes. NumPy dispone de numpy.round, y pandas del método round() en series y DataFrames, que incluso admite un diccionario con los decimales de cada columna:
import numpy as np
import pandas as pd
arr = np.array([1.005, 2.675, 3.14159])
print(np.round(arr, 2))
df = pd.DataFrame({"precio": [19.987, 5.5049], "descuento": [0.12345, 0.0789]})
print(df.round({"precio": 2, "descuento": 3}))
[1. 2.68 3.14]
precio descuento
0 19.99 0.123
1 5.50 0.079
Ambas librerías usan el redondeo al par y trabajan con números de punto flotante, por lo que presentan las mismas peculiaridades que hemos visto. Además, el redondeo de NumPy usa un algoritmo de escalado que puede diferir del de la función integrada en casos límite: en el ejemplo, np.round devuelve 2.68 para 2.675, mientras que round(2.675, 2) devuelve 2.67.
Una recomendación importante: redondea solo al final, al presentar los resultados. Redondear en pasos intermedios de un cálculo acumula errores que pueden distorsionar las conclusiones.
Casos de uso reales: dinero, porcentajes y medidas
Las necesidades de redondeo cambian según el dominio, y elegir la herramienta adecuada depende de ello.
En aplicaciones financieras, lo más robusto es evitar los float por completo. Una opción es usar Decimal en todo el flujo; otra, muy popular en sistemas de pagos, es almacenar los importes como enteros en la unidad más pequeña, por ejemplo céntimos, y convertir a decimales solo al mostrarlos:
precio_centimos = 1999 # 19.99
cantidad = 3
total_centimos = precio_centimos * cantidad
print(f"Total: {total_centimos // 100}.{total_centimos % 100:02d}")
Total: 59.97
Calcular un impuesto sobre una factura con varias líneas ilustra otra decisión de negocio: redondear por línea o sobre el total puede dar resultados diferentes en un céntimo, y la normativa suele indicar cuál aplicar:
from decimal import Decimal, ROUND_HALF_UP
CENT = Decimal("0.01")
lineas = [Decimal("10.05"), Decimal("20.15"), Decimal("5.35")]
tasa = Decimal("0.21")
por_linea = sum((l * tasa).quantize(CENT, ROUND_HALF_UP) for l in lineas)
sobre_total = (sum(lineas) * tasa).quantize(CENT, ROUND_HALF_UP)
print(por_linea, sobre_total)
7.46 7.47
Con solo tres líneas, ambos métodos ya difieren en un céntimo. Por eso conviene definir la regla explícitamente, documentarla y cubrirla con pruebas.
Para porcentajes que deben sumar exactamente cien, como los de una encuesta, redondear cada valor de forma independiente puede dar 99 o 101. El método del mayor residuo reparte la diferencia entre los valores con mayor parte decimal:
import math
def porcentajes_que_suman_100(valores):
total = sum(valores)
brutos = [v * 100 / total for v in valores]
enteros = [math.floor(p) for p in brutos]
faltan = 100 - sum(enteros)
orden = sorted(range(len(brutos)), key=lambda i: brutos[i] - enteros[i], reverse=True)
for i in orden[:faltan]:
enteros[i] += 1
return enteros
print(porcentajes_que_suman_100([1, 1, 1]))
[34, 33, 33]
En ciencia e ingeniería, en cambio, lo que importa suele ser el número de cifras significativas y no los decimales. El especificador g del formato de cadenas resuelve ese caso:
print(f"{0.000123456:.3g}", f"{123456.789:.3g}")
0.000123 1.23e+05
Errores comunes y cómo solucionarlos
Hay errores que se repiten en casi todos los proyectos que manejan números con decimales.
El primero es usar round() para mostrar importes. Como no añade ceros, un precio de 5.5 aparece como 5.5 en lugar de 5.50. La solución es redondear para calcular y formatear para mostrar, con f"{valor:.2f}".
El segundo es comparar resultados de punto flotante con ==. Expresiones como 0.1 + 0.2 == 0.3 devuelven False. Usa math.isclose() o trabaja con Decimal.
El tercero es crear un Decimal a partir de un float. Decimal(2.675) arrastra la imprecisión binaria. Pasa siempre una cadena: Decimal("2.675").
El cuarto es mezclar tipos. Python no permite operar directamente entre Decimal y float:
TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'
La solución es convertir el float a cadena y después a Decimal, o mantener todo el cálculo en un único tipo.
El quinto es intentar redondear datos que llegan como texto, algo habitual al leer formularios, archivos CSV o respuestas de APIs:
round("3.14159", 2)
TypeError: type str doesn't define __round__ method
Convierte primero el valor con float() o Decimal(), y valida la entrada para capturar los valores no numéricos.
El sexto es usar quantize() con un exponente que excede la precisión del contexto, lo que provoca una excepción InvalidOperation. Si necesitas muchos decimales, aumenta la precisión con localcontext() antes de cuantizar.
El séptimo es redondear demasiado pronto. Redondear cada paso intermedio de un cálculo acumula errores; es preferible operar con toda la precisión disponible y redondear una única vez al final.
Probar el redondeo con pruebas automatizadas
El redondeo es una de esas áreas donde los errores pasan desapercibidos durante meses y aparecen de repente en una auditoría o en la queja de un cliente. Por eso merece pruebas específicas que fijen el comportamiento esperado, sobre todo en los casos límite.
La clave está en elegir bien los datos de prueba. No basta con valores cómodos como 3.14159; hay que incluir empates exactos, números que no tienen representación binaria exacta, valores negativos, ceros y cantidades muy grandes. Con pytest y su decorador de parametrización, una sola prueba cubre todos esos casos de forma legible:
# test_dinero.py
from decimal import Decimal
import pytest
from dinero import a_dinero
@pytest.mark.parametrize(
("entrada", "esperado"),
[
("2.675", "2.68"), # empate que float redondea mal
("2.665", "2.67"), # empate con dígito par
("0.005", "0.01"), # valor muy pequeño
("-1.005", "-1.01"), # negativo: HALF_UP se aleja de cero
("0", "0.00"), # cero con dos decimales
("1234567.895", "1234567.90"),
],
)
def test_a_dinero(entrada, esperado):
assert a_dinero(entrada) == Decimal(esperado)
pip install pytest
pytest -q
...... [100%]
6 passed in 0.02s
Fíjate en el caso negativo. Con ROUND_HALF_UP, el módulo decimal redondea los empates alejándose de cero, de modo que -1.005 se convierte en -1.01. Si tu normativa exige otro comportamiento para los importes negativos, como en las devoluciones, esta prueba lo dejará documentado y evitará cambios accidentales.
Para cálculos más complejos, las pruebas basadas en propiedades son un complemento excelente. La librería Hypothesis genera cientos de valores aleatorios y comprueba que se cumplan ciertas reglas, por ejemplo que el resultado redondeado nunca se aleje del original más de medio céntimo:
from decimal import Decimal
from hypothesis import given, strategies as st
from dinero import a_dinero
@given(st.decimals(min_value=-10**6, max_value=10**6, places=6, allow_nan=False))
def test_diferencia_maxima(valor):
assert abs(a_dinero(str(valor)) - valor) <= Decimal("0.005")
Este tipo de pruebas suele descubrir casos que nadie había previsto, como valores con muchos decimales o en notación científica.
Rendimiento: cuándo usar cada enfoque
El módulo decimal está implementado en C desde hace muchas versiones y es razonablemente rápido, pero sigue siendo más lento que la aritmética nativa de float, que el procesador ejecuta directamente en hardware. Puedes medirlo con el módulo timeit:
python -m timeit -s "x = 2.675" "round(x, 2)"
python -m timeit -s "from decimal import Decimal; x = Decimal('2.675'); c = Decimal('0.01')" "x.quantize(c)"
Los resultados varían según el equipo, pero la diferencia suele ser de unas pocas veces a favor de float. En la práctica, esa diferencia solo es relevante en bucles con millones de operaciones o en cálculos científicos intensivos, donde además NumPy ofrece un rendimiento muy superior gracias a la vectorización.
La regla práctica queda así:
- Para mostrar valores, usa f-strings con
.2f. - Para cálculos generales, científicos o estadísticos, usa
floatyround(), redondeando al final. - Para dinero, impuestos o cualquier cálculo con requisitos legales, usa
Decimalcon un modo de redondeo explícito, o enteros en céntimos. - Para grandes volúmenes de datos numéricos, usa NumPy o pandas.
Conclusiones
Redondear a dos decimales en Python es sencillo cuando se elige la herramienta adecuada para cada objetivo. La función round() resuelve la mayoría de los casos de cálculo general, pero conviene recordar que aplica el redondeo al par en los empates exactos y que trabaja sobre el valor binario real del número, lo que explica resultados como round(2.675, 2) == 2.67.
Cuando lo que se necesita es presentar un número, las f-strings con el especificador .2f son la opción correcta: siempre muestran dos decimales, admiten separadores de miles, porcentajes y alineación, y devuelven una cadena lista para la interfaz.
Para dinero y cualquier cálculo con requisitos contables, el módulo decimal ofrece exactitud en base diez y control total sobre el modo de redondeo mediante quantize(). Crear los valores a partir de cadenas y centralizar el redondeo en una función auxiliar evita la mayoría de los problemas.
Entender cómo funcionan los números de punto flotante, cuándo redondear y con qué estrategia marca la diferencia entre un informe que cuadra al céntimo y uno que genera incidencias difíciles de rastrear. Con las técnicas de este artículo tienes todo lo necesario para tomar esa decisión con criterio en tus proyectos.
