2011-05-13 17 views
17

No sé si esto es un error obvio, pero al ejecutar un script 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 Python:Error de redondeo de Python con números flotantes

for i_delta in range(0, 101, 1): 
    delta = float(i_delta)/100 

    (...) 

filename = 'foo' + str(int(delta * 100)) + '.dat' 

genera archivos idénticos para delta = 0,28 y 0,29, lo mismo con 0,57 y 0,58, siendo la razón que vuelve pitón flotador (29)/100 como ,28999999999999998 . Pero eso no es un error sistemático, no en el sentido en que le sucede a cada entero. Así que creé la siguiente secuencia de comandos 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 ocurre este error de redondeo. ¿Por qué sucede esto con esos números particulares?

+4

Así es como funcionan los números flotantes IEEE 754. Te sugiero que vuelvas para convertir el flotador en un número entero, en lugar de simplemente truncar. –

+1

No es un error, es común en muchos idiomas diferentes. Hay algunas caminatas, pero en este caso la solución más simple puede ser simplemente usar idelta en nombre de archivo. Solo tenga en cuenta que idelta no se pasa fuera del ciclo por defecto. – Tadeck

+2

# StdSOAnswer_1. Así es como funciona el punto flotante. –

Respuesta

27

Cualquier número que no se pueda generar con las potencias exactas de dos no se puede representar exactamente como un número de coma flotante; necesita ser aproximado. A veces, la aproximación más cercana será menor que el número real.

Lea What Every Computer Scientist Should Know About Floating-Point Arithmetic.

+0

Honestamente, no vi su enlace al mismo documento antes de publicar el mismo enlace. Solo muestra su buena referencia. –

+0

@jimbob, agregué el enlace un minuto después de la publicación original. Es un clásico, pero no lo tuve de inmediato útil. –

+5

Para Pythonistas, también hay un capítulo más corto (y más fácil de leer) en el [Tutorial de Python] (http://docs.python.org/tutorial/floatingpoint.html) que trata este tema. –

16

Es muy conocido debido a la naturaleza de floating point numbers.

Si desea hacer aritmética decimal no aritmética de coma flotante hay libraries para hacer esto.

Eg,

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

En decimal general es, probablemente, va por la borda y todavía tendrá errores de redondeo en casos raros cuando el número no tiene una representación decimal finito (por ejemplo, cualquier fracción donde el denominador no es 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 siempre a la vuelta antes de emitir puntos flotantes de enteros, a menos que desee una función piso.

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

Otra alternativa es utilizar la clase Fracción de la biblioteca fractions que no aproximada. (Justs sigue agregando/restando y multiplicando los numeradores y denominadores enteros según sea necesario).

+0

hmm, en realidad un mejor ejemplo es Decimal (1)/Decimal (3) * Decimal (3), que no produce 1.0 con más precisión. "cuando la base no es 10" debería ser cuando la fracción no se puede representar en la base 10 exactamente. El número es, por supuesto, base 10. –

+0

@DerekLitz - De acuerdo, mi respuesta fue descuidada.Su ejemplo es más conciso (aunque ambos son igualmente válidos). Debería haber escrito cuando el número no tiene una representación decimal finita en base-10, lo que ocurrirá con cualquier fracción cuando el denominador no sea divisible entre 2 o 5. (De acuerdo "la fracción no puede ser representada exactamente en la base 10. es, por supuesto, en la base 10. "tampoco es exactamente correcto. Los números no tienen bases. Un tercio es 1/(1 + 1 + 1) exactamente independientemente de la base. Escrito como una fracción, puede estar en representado en base 10--1/3.) –

+0

@dr_jimbob Me gusta la mejora anterior, sin embargo, no me gusta la afirmación 'los números no tienen bases'. Quizás la diferencia sea semántica, pero el significado de las palabras es importante. Se supone que un número representa un valor (o cantidad, si lo prefiere). Para crear un sistema de numeración, se debe elegir una base, se deben elegir símbolos y podemos comunicarnos de manera más efectiva que conteo simple, pero estoy seguro de que eso es lo que querías decir :) –

Cuestiones relacionadas