Error de redondeo de Python con números flotantes

No sé si este es un error obvio, pero al ejecutar un script de Python para variar los parámetros de una simulación, me di cuenta de que faltaban los resultados con delta = 0.29 y delta = 0.58. En la investigación, me di cuenta de que el siguiente código de Python:

for i_delta in range(0, 101, 1): delta = float(i_delta) / 100 (...) filename = 'foo' + str(int(delta * 100)) + '.dat' 

generaron archivos idénticos para delta = 0.28 y 0.29, lo mismo con .57 y .58, la razón es que python devuelve float (29) / 100 como 0.28999999999999998. Pero eso no es un error sistemático, no en el sentido en que le sucede a todos los enteros. Así que creé el siguiente script de Python:

 import sys n = int(sys.argv[1]) for i in range(0, n + 1): a = int(100 * (float(i) / 100)) if i != a: print i, a 

Y no puedo ver ningún patrón en los números para los que se produce este error de redondeo. ¿Por qué sucede esto con esos números en particular?

Cualquier número que no pueda construirse a partir de potencias exactas de dos no puede representarse exactamente como un número de punto flotante; necesita ser aproximada A veces, la aproximación más cercana será menor que el número real.

Lea lo que todo científico informático debe saber sobre la aritmética de punto flotante .

Es muy conocido debido a la naturaleza de los números de punto flotante .

Si quieres hacer aritmética decimal y aritmática de punto flotante, hay bibliotecas para hacer esto.

P.ej,

 >>> from decimal import Decimal >>> Decimal(29)/Decimal(100) Decimal('0.29') >>> Decimal('0.29')*100 Decimal('29') >>> int(Decimal('29')) 29 

En general, es probable que el decimal esté por la borda y aún tendrá errores de redondeo en casos excepcionales cuando el número no tenga una representación decimal finita (por ejemplo, cualquier fracción donde el denominador no sea 1 o divisible por 2 o 5, los factores de la base decimal) (10)). Por ejemplo:

 >>> s = Decimal(7) >>> Decimal(1)/s/s/s/s/s/s/s*s*s*s*s*s*s*s Decimal('0.9999999999999999999999999996') >>> int(Decimal('0.9999999999999999999999999996')) 0 

Así que es mejor redondear siempre antes de lanzar puntos flotantes a ints, a menos que desee una función de piso.

 >>> int(1.9999) 1 >>> int(round(1.999)) 2 

Otra alternativa es usar la clase de Fracción de la biblioteca de fracciones que no se aproxima. (Se justs sigue sumndo / restando y multiplicando los numeradores enteros y denominadores según sea necesario).