Por qué 0,1 + 0,2 no da 0,3 y cuándo sumar céntimos con floats sale exacto
Un análisis del formato de doble precisión explica el patrón que decide cuándo la suma de importes en coma flotante cuadra al céntimo y cuándo se desvía.
Que 0,1 + 0,2 no dé 0,3 en coma flotante de doble precisión es de las primeras cosas que aprende cualquiera que toque un lenguaje de programación, y una de las que nunca se olvidan. El valor exacto que devuelve el intérprete es 0,30000000000000004. La conclusión habitual es que el dinero no se suma con floats, sino con decimales o con enteros de céntimos, y ahí no hay discusión seria. Lo que pone encima de la mesa un análisis del formato es que, en la práctica, la suma de céntimos sale exacta mucho más a menudo de lo que sugiere la fama del float.
Si se recorren todos los pares de múltiplos de 0,01 hasta 1,00 y se compara el resultado impreso con la suma exacta, aparece un patrón: unos pares quedan por debajo, otros por encima y una mayoría cuadra. No es una casualidad ni un detalle de una implementación concreta.
Qué hace un double con tus céntimos
Un número de doble precisión IEEE se guarda en 64 bits: 1 de signo, 11 de exponente y 52 de fracción. Con el bit oculto, el valor es ±2^E(1 + F/2^52), y el peso efectivo del último bit de fracción —el ulp, unit in the last place— vale 2^(E−52). Ahí está la clave: el hueco entre un float y el siguiente depende del exponente, no de cuántos decimales te apetezca escribir. En el rango de los céntimos ese hueco es varias veces más fino que 0,01, y por eso muchas sumas caen justo donde deben caer.
Al sumar, el cálculo se hace como si hubiera precisión infinita y luego se redondea al float más cercano, con los empates resueltos hacia la mantisa par. Ese último detalle es el que decide a qué lado cae 0,1 + 0,2, que está exactamente a medio camino entre dos representables.
Imprimir el resultado es otra historia. La conversión de binario a decimal no la fija el estándar, y durante años Python arrastró el problema de que teclear 1.1 lo devolvía como 1.1000000000000001. Se corrigió en la versión 3.1 siguiendo el criterio que Steele y White formalizaron en 1990: el decimal tiene que poder reparsearse al mismo float, ser el más corto posible y, entre los que cumplen lo anterior, el más cercano al valor real. Con esas tres reglas, 0,30000000000000004 es exactamente lo que toca imprimir.
Hasta dónde llega el margen
Para importes positivos por debajo de 2^46, esto es 70.368.744.177.664, unos 70 billones de dólares, los representables son más densos que los propios céntimos. Por encima de esa cota el formato se vuelve más grueso que el céntimo y la aritmética deja de sostener la promesa. Es una frontera muy por encima de cualquier factura doméstica, pero conviene tenerla presente antes de reescribir una regla de estilo por lo que diga un caso aislado.
Nada de esto invalida la recomendación de no usar floats para contabilidad: sigue siendo correcto usar decimales o céntimos enteros. Lo que cambia es la lectura del riesgo. Si solo hay que sumar y restar importes pequeños, el error no aparece; si hay repartos, porcentajes o acumulaciones largas, aparece. Para lo primero, el módulo fractions de la biblioteca estándar de Python da una salida exacta sin montar nada.


