17 Stimmen

Unkorrekter Logarithmus in Python

Ich arbeite täglich mit Python 2.4 in meinem Unternehmen. Ich habe die vielseitige Logarithmusfunktion 'log' aus der Standard-Mathematik-Bibliothek verwendet, und als ich log(2**31, 2) eingegeben habe, hat sie 31.000000000000004 zurückgegeben, was mich etwas seltsam erschien.

Ich habe dasselbe mit anderen Potenzen von 2 gemacht, und es hat einwandfrei funktioniert. Ich habe 'log10(2**31) / log10(2)' ausgeführt und 31.0 erhalten.

Ich habe versucht, dieselbe originale Funktion in Python 3.0.1 auszuführen, in der Hoffnung, dass es in einer fortschrittlicheren Version behoben wurde.

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

0 Stimmen

Duplikat der immerwährenden Frage zu Gleitkommazahlen (warum erhalte ich Gleitkommazerros?), kann die beste Duplikatfrage nicht finden, vielleicht kann jemand anderes helfen.

0 Stimmen

Ich möchte darauf hinweisen, dass Python 3 den Gleitkommafehler nicht behoben hat. Stattdessen verwendet die Druckausgabe einen intelligenten Algorithmus, um den beabsichtigten Gleitkommawert anzuzeigen, anstelle des Fehlers.

2voto

quant_dev Punkte 6081

Dies 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 könnte es eine Hardwareunterstützung für die Berechnung von Basis-10-Logarithmen geben.

2voto

aka.nice Punkte 8760

Floats sind ungenau

Ich kaufe dieses Argument nicht, denn exakte Zweierpotenzen werden auf den meisten Plattformen genau dargestellt (mit dem zugrunde liegenden IEEE 754 Floating-Point-Format).

Also, wenn wir wirklich wollen, dass log2 einer exakten Zweierpotenz genau ist, können wir das tun.
Ich werde es in Squeak Smalltalk demonstrieren, weil es leicht ist, das Basissystem in dieser Sprache zu ändern, aber die Sprache spielt keine Rolle, Floating-Point-Berechnungen sind universell, und das Python-Objektmodell ist nicht weit von Smalltalk entfernt.

Für das Berechnen des Logarithmus zur Basis n gibt es die Funktion log:, die in Number definiert ist und naiverweise den natürlichen Logarithmus ln verwendet:

log: aNumber 
    "Gibt den Logarithmus zur Basis aNumber des Empfängers zurück."
    ^self ln / aNumber ln

self ln (nimmt den natürlichen Logarithmus des Empfängers) , aNumber ln und / sind drei Operationen, die ihr Ergebnis auf die nächste Float-Zahl runden, und diese Rundungsfehler können sich akkumulieren.... Also die naive Implementierung ist anfällig für die von dir beobachteten Rundungsfehler, und ich vermute, dass die Python-Implementierung der Logarithmus-Funktion nicht viel anders ist.

((2 raisedTo: 31) log: 2) = 31.000000000000004

Aber wenn ich die Definition so ändere:

log: aNumber 
    "Gibt den Logarithmus zur Basis aNumber des Empfängers zurück."
    aNumber = 2 ifTrue: [^self log2].
    ^self ln / aNumber ln

generic liefere ein log2 in der Number-Klasse:

log2
    "Gibt den Logarithmus zur Basis 2 des Empfängers zurück."
    ^self asFloat log2

und diese Verfeinerung in der Float-Klasse:

log2
    "Gibt den Logarithmus zur Basis 2 des Empfängers zurück.
    Achtet darauf, das genaue Ergebnis für exakte Zweierpotenzen geliefert wird."
    ^self significand ln / Ln2 + self exponent asFloat

wo Ln2 eine Konstante (2 ln) ist, bekomme ich tatsächlich ein genaues log2 für exakte Zweierpotenzen, weil der Signifikant einer solchen Zahl = 1.0 ist (einschließlich Subnormalität gemäß der Squeak-Exponent/Signifikant-Definition), und 1.0 ln = 0.0.

Die Implementierung ist ziemlich trivial und sollte ohne Schwierigkeiten in Python übersetzt werden können (wahrscheinlich in der VM); die Laufzeitkosten sind sehr gering, es kommt also nur darauf an, wie wichtig wir diese Funktion finden oder nicht.

Wie ich immer sage, die Tatsache, dass die Ergebnisse von Gleitkommaoperationen auf den nächsten (oder eine beliebige andere) darstellbaren Wert gerundet werden, ist keine Lizenz zum Verschwenden von ulp. Genauigkeit hat einen Preis, sowohl in Bezug auf Laufzeitstrafen als auch auf Implementierungskomplexität, also wird es durch Abwägungen gesteuert.

1voto

tzot Punkte 86792

Die repräsentation (float.__repr__) einer Zahl in Python versucht, einen String von Ziffern zurückzugeben, der so nah wie möglich am tatsächlichen Wert liegt, wenn er zurückkonvertiert wird, vorausgesetzt, die IEEE-754-Arithmetik ist bis zu einem gewissen Limit genau. In jedem Fall würden Sie es nicht bemerken, wenn Sie das Ergebnis printen:

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

print konvertiert seine Argumente in Zeichenketten (in diesem Fall durch die float.__str__-Methode), die sich durch Anzeige weniger Ziffern auf die Ungenauigkeit einstellen:

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

Die Antwort von usuallyuseless ist tatsächlich sehr nützlich :)

1voto

Developer Punkte 337

Wenn Sie den höchsten Exponenten von 'k' in einer Zahl 'n' berechnen möchten. Dann könnte der folgende Code hilfreich sein:

import math
antwort = math.ceil(math.log(n,k))
while k**antwort>n:
    antwort-=1

HINWEIS: Sie sollten nicht 'if' anstelle von 'while' verwenden, da dies in einigen Fällen zu falschen Ergebnissen führen kann, z.B. wenn n=2**51-1 und k=2. In diesem Beispiel ist das Ergebnis mit 'if' 51, während das Ergebnis mit 'while' 50 ist, was korrekt ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X