2009-05-31 5 views
8

Ich arbeite täglich mit Python 2.4 bei meiner Firma. Ich benutzte die vielseitige Logarithmusfunktion 'log' aus der Standard-Mathematikbibliothek, und als ich log (2 ** 31, 2) eingab, gab es 31,000000000000004 zurück, was mir etwas merkwürdig vorkam.Inakkurate Logarithmus in Python

Ich tat das gleiche mit anderen Potenzen von 2, und es hat perfekt funktioniert. Ich lief 'log10 (2 ** 31)/log10 (2)' und ich bekam eine Runde 31.0

Ich versuchte, die gleiche ursprüngliche Funktion in Python 3.0.1 ausgeführt, vorausgesetzt, dass es in einer erweiterten Version behoben wurde.

Warum passiert das? Ist es möglich, dass es in Python einige Ungenauigkeiten in mathematischen Funktionen gibt?

+0

Duplikat der ewigen Frage Floating-Point (? warum bin ich Gleitkommafehler bekommen), kann nicht die besten doppelten Q finden zu schreiben, vielleicht kann jemand anderes. –

+0

Ich sollte darauf hinweisen, dass Python 3 * nicht * den Gleitkommafehler behoben hat. Stattdessen verwendet die Druckausgabe einen intelligenten Algorithmus, um den beabsichtigten Gleitkommawert anstelle des Durchhangs anzuzeigen. –

Antwort

44

Dies ist mit Computerarithmetik zu erwarten. Es folgt bestimmten Regeln wie IEEE 754, die wahrscheinlich nicht die Mathematik entsprechen, die Sie in der Schule gelernt haben.

Wenn diese tatsächlich Angelegenheiten verwenden Python decimal type.

Beispiel:

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 für eine nette Antwort, aber meistens für "Wenn das wirklich zählt." Sonden wurden mit weniger Präzision zum Saturn geflogen. – dwc

+0

In der Tat. Die Kursivschrift ist der wichtigste Teil der Antwort. –

+0

@dwc Es wäre wichtig gewesen, wenn das OP das Ergebnis der Protokollfunktion genommen hätte. Dann würde der Fehler sehr groß werden. In meinem Fall habe ich in einem meiner Programme folgendes gemacht: 'a = floor (log (x, b))' und das Programm stürzte ein paar Zeilen weiter, weil 'floor (log (243,3))' war herauskommen zu sein 4 – Rushil

17

immer davon ausgehen, dass Gleitkommaoperationen in ihnen einige Fehler haben und für die Gleichstellung überprüfen, dass Fehler zu berücksichtigen (entweder ein Prozentwert wie 0,00001% oder einen festen Wert wie ,00000000001 Einnahme). Diese Ungenauigkeit ist gegeben, da nicht alle Dezimalzahlen binär mit einer festen Anzahl von Bits Genauigkeit dargestellt werden können.

Ihr besonderer Fall ist keiner von ihnen, wenn Python IEEE754 verwendet, da 31 mit einfacher einfacher Genauigkeit leicht darstellbar sein sollte. Es ist jedoch möglich, dass es Genauigkeit verliert in einem der vielen Schritte, die es braucht, um zu berechnen, einfach, weil es keinen Code hat, um Sonderfälle wie eine direkte Zweierpotenz zu erkennen.

+1

+1 Ich denke, Sie haben einen Griff mit Ihrem letzten Satz. –

+0

Sehr interessant. Ich schreibe sehr lange Code, und das ist das erste Mal, dass ich auf dieses Phänomen gestoßen bin. Aber nach der Antwort hier, denke ich, beginne ich jetzt das größere Bild zu sehen, warum es passiert. –

5

Fließkommaoperationen sind nie exakt. Sie geben ein Ergebnis mit einem akzeptablen relativen Fehler für die Sprache/Hardware-Infrastruktur zurück.

Im Allgemeinen ist es falsch anzunehmen, dass Gleitkommaoperationen präzise sind, insbesondere mit einfacher Genauigkeit. "Accuracy problems" section aus Wikipedia Fließkomma Artikel :)

2

Das ist normal. Ich würde erwarten, dass log10 genauer ist als log (x, y), da es genau weiß, was die Basis des Logarithmus ist, auch kann es eine Hardware-Unterstützung für die Berechnung von Logarithmen der Basis 10 geben.

3

IEEE Doppelpunktzahlen haben floating 52 bits of precision. Seit 10^15 < 2^52 < 10^16 hat ein Doppel zwischen 15 und 16 signifikanten Ziffern. Das Ergebnis 31.000000000000004 ist korrekt auf 16 Ziffern, also ist es so gut wie man es erwarten kann.

1

Die repr esentation (float.__repr__) einer Anzahl in Python versucht, eine Zeichenfolge von Ziffern, so nahe an der realen Wert wie möglich zurückzukehren, wenn zurückführt, da die IEEE-754-Arithmetik bis zu einer Grenze genau ist.Auf jeden Fall, wenn Sie print das Ergebnis ed, würden Sie nicht bemerken:

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

print konvertiert ihre Argumente in Strings (in diesem Fall durch die float.__str__ Methode), die durch die Anzeige weniger Stellen für die Ungenauigkeit bietet :

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

usuallyuseless' Antwort ist sehr nützlich, eigentlich :)