Nunca uses float para dinero: un presupuesto reproducible con Decimal
Los errores de redondeo parecen pequeños hasta que se acumulan en reportes, presupuestos y saldos. El modelo numérico importa incluso en una herramienta personal sin conexiones bancarias.
El problema y el alcance del ejemplo
La ficha de Vault Wallet: Control Gastos resume su función así: «Gestor de gastos y planificador de presupuesto para tus finanzas personales.» Vault Wallet anuncia registro de gastos, presupuestos y manejo de varias monedas. El ejemplo toma una decisión que se puede evaluar sin conocer el código privado de la app: usar decimales explícitos y no sumar divisas diferentes.
Cómo reproducir la idea
Necesitas Python 3; pega este código en presupuesto.py y ejecútalo con python presupuesto.py.
from decimal import Decimal
movimientos = [
{"categoria": "comida", "moneda": "MXN", "importe": "129.90"},
{"categoria": "comida", "moneda": "MXN", "importe": "70.10"},
{"categoria": "transporte", "moneda": "MXN", "importe": "35.50"},
{"categoria": "servicios", "moneda": "USD", "importe": "9.99"},
]
limites = {("comida", "MXN"): Decimal("250.00")}
totales = {}
for m in movimientos:
clave = (m["categoria"], m["moneda"])
totales[clave] = totales.get(clave, Decimal("0")) + Decimal(m["importe"])
for clave, gastado in sorted(totales.items()):
limite = limites.get(clave)
print(clave, f"gastado={gastado:.2f}",
f"restante={limite-gastado:.2f}" if limite is not None else "sin límite")
Qué demuestra el código
El total en comida es exactamente 200.00 MXN. Si usamos números binarios de punto flotante pueden aparecer representaciones intermedias como 0.30000000000000004. Agrupar por (categoría, moneda) evita que un total en pesos incorpore dólares silenciosamente.
Pruebas recomendadas
Cambia el segundo gasto a 120.10: el presupuesto queda en negativo y puede convertirse en alerta. Agrega otra operación en USD: la suma se conserva separada. Prueba también importes negativos para devoluciones, definiendo explícitamente cómo deben tratarse.
Límites que conviene reconocer
No hay persistencia, impuestos, tasas de cambio, sincronización ni reglas fiscales. En producción hay que fijar políticas de redondeo, moneda principal, fechas, devoluciones y versiones de tasas antes de generar reportes.
Qué aprendemos
No todo se arregla con una pantalla de presupuesto. La precisión monetaria y la semántica de cada operación forman parte del modelo de datos.
Contexto y enlaces del autor
El tema se relaciona con las funciones declaradas públicamente de Vault Wallet: Control Gastos, una aplicación Android de AppInitDev. El código anterior es un ejemplo independiente: no se ha inspeccionado el repositorio privado ni se afirma que sea la implementación interna de la app. La referencia aparece al final para ofrecer contexto, no como requisito para completar el tutorial.
- Google Play: Vault Wallet: Control Gastos
- Página de la aplicación: https://wallet.appinitdev.com/
- Sitio del desarrollador: AppInitDev

Top comments (1)
Sumar Decimal armados desde cadenas como "129.90" sí es exacto. El problema aparece cuando se divide una cuenta o se aplica un impuesto: el resultado trae más decimales que la moneda. Hay que pasar quantize(Decimal("0.01")) antes de sumarlo de nuevo. Si no, esos dígitos entran al siguiente total, y el {:.2f} solo los esconde al imprimir.
Ojo con la entrada: Decimal(float(monto)) se queda con el error binario. El monto tiene que llegar como cadena.