2009-05-31 8 views
8

Trabajo a diario con Python 2.4 en mi empresa. Utilicé la función de logaritmo versátil 'log' de la biblioteca matemática estándar, y cuando ingresé el registro (2 ** 31, 2) devolvió 31.000000000000004, lo que me pareció un poco extraño.Logaritmo inexacto en Python

Hice lo mismo con otras potencias de 2, y funcionó perfectamente. Ejecuté 'log10 (2 ** 31)/log10 (2)' y obtuve una ronda 31.0

Intenté ejecutar la misma función original en Python 3.0.1, suponiendo que se solucionó en una versión más avanzada.

¿Por qué sucede esto? ¿Es posible que haya algunas imprecisiones en las funciones matemáticas en Python?

+0

duplicado de la pregunta perenne de punto flotante (¿por qué recibo errores de punto flotante), no puede encontrar el mejor Q duplicado para publicar, tal vez alguien más pueda. –

+0

Debo señalar que Python 3 no * * solucionó el error de coma flotante. En cambio, la salida de impresión utiliza un algoritmo inteligente para mostrar el valor del punto flotante deseado, en lugar de la holgura. –

Respuesta

44

Esto es de esperar con la aritmética de la computadora. Sigue reglas particulares, como IEEE 754, que probablemente no coincidan con las matemáticas que aprendió en la escuela.

Si esto realmente importa , utilice Python's decimal type.

Ejemplo:

from decimal import Decimal, Context 
ctx = Context(prec=20) 
two = Decimal(2) 
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx)) 
+22

+1 por una buena respuesta, pero principalmente por "Si esto realmente importa". Las sondas fueron enviadas a Saturno con menos precisión. – dwc

+0

De hecho. La cursiva es la parte más importante de la respuesta. –

+0

@dwc Hubiera sido importante si el OP hubiera estado cerrando el resultado de la función de registro. Entonces el error sería muy grande. En mi caso, en uno de mis programas, estaba haciendo esto: 'a = floor (log (x, b))' y el programa se colgaba unas pocas líneas adelante porque 'floor (log (243,3))' era saliendo a ser 4 – Rushil

17

Siempre asumir que las operaciones de punto flotante tendrá algún error en ellos y comprobar la igualdad de tomar que el error en cuenta (ya sea un valor de porcentaje como 0,00001% o un valor fijo como ,00000000001) Esta inexactitud es un hecho ya que no todos los números decimales pueden representarse en binario con un número fijo de bits de precisión.

Su caso particular no es uno de ellos si Python usa IEEE754 ya que 31 debería ser fácilmente representable con una sola precisión. Sin embargo, es posible que pierda precisión en uno de los muchos pasos necesarios para calcular el registro , simplemente porque no tiene código para detectar casos especiales como una potencia directa de dos.

+1

+1 Creo que tienes un control con tu última oración. –

+0

Muy interesante. Escribo código por mucho tiempo, y esa es la primera vez que me encuentro con este fenómeno. Pero después de la respuesta aquí, creo que ahora empiezo a ver una imagen más completa de por qué sucede. –

5

operaciones de punto flotante nunca son exactas. Devuelven un resultado que tiene un error relativo aceptable para la infraestructura de idioma/hardware.

En general, es un error suponer que las operaciones de coma flotante son precisas, especialmente con precisión simple. "Accuracy problems" section de Wikipedia Artículo de punto flotante :)

2

Esto es normal. Esperaría que log10 sea más preciso que log (x, y), ya que sabe exactamente cuál es la base del logaritmo, también puede haber algo de soporte de hardware para calcular los logaritmos de base 10.

3

números de punto flotante IEEE dobles tienen 52 bits of precision. Desde 10^15 < 2^52 < 10^16, un doble tiene entre 15 y 16 cifras significativas. El resultado 31.000000000000004 es correcto a 16 cifras, por lo que es tan bueno como puede esperar.

1

El repr esentation (float.__repr__) de un número en pitón trata de devolver una cadena de dígitos como cerca del valor real como sea posible cuando se convierte de nuevo, dado que IEEE-754 aritmética es preciso hasta un límite.En cualquier caso, si el resultado print Ed, usted no notará:

>>> from math import log 
>>> log(2**31,2) 
31.000000000000004 
>>> print log(2**31,2) 
31.0 

print convierte sus argumentos para cuerdas (en este caso, a través del método float.__str__), que atiende a la inexactitud exhibiendo menos dígitos :

>>> log(1000000,2) 
19.931568569324174 
>>> print log(1000000,2) 
19.9315685693 
>>> 1.0/10 
0.10000000000000001 
>>> print 1.0/10 
0.1 

usuallyuseless respuesta es muy útil, en realidad :)

Cuestiones relacionadas