Hier ist eine benutzerdefinierte Funktion, die das Durchlaufen von Dezimalschritten ermöglicht:
%Vor%Es funktioniert so:
%Vor% Nun, es gibt nichts Überraschendes daran. Es ist verständlich, dass dies aufgrund von Gleitkomma-Ungenauigkeiten geschieht und dass 0.1
keine exakte Darstellung im Speicher hat. Diese Genauigkeitsfehler sind also verständlich.
Nimm numpy
auf der anderen Seite:
Interessant ist, dass hier keine sichtbaren Ungenauigkeitsgenauigkeiten auftreten. Ich dachte, das könnte etwas mit dem __repr__
zu tun haben. Um das zu bestätigen, habe ich Folgendes versucht:
Also gibt meine Funktion einen falschen oberen Wert zurück (es sollte 1.0
sein, aber es ist tatsächlich 1.0999999999999999
), aber np.arange
macht es richtig.
Mir ist bekannt Ist Fließkomma-Mathematik gebrochen? aber der Punkt von Diese Frage ist:
Der Unterschied zwischen den Endpunkten liegt darin, dass NumPy die Länge vorne statt ad hoc berechnet, da das Array vorher zugewiesen werden muss. Sie können dies im _calc_length
Helfer sehen . Anstatt zu stoppen, wenn es das end-Argument trifft, stoppt es, wenn es die vorbestimmte Länge erreicht.
Die Berechnung der Länge vor der Front schützt Sie nicht vor den Problemen eines nicht ganzzahligen Schritts, und Sie erhalten häufig den "falschen" Endpunkt, zum Beispiel mit numpy.arange(0.0, 2.1, 0.3)
:
Es ist viel sicherer, numpy.linspace
zu verwenden, wo Sie statt der Schrittgröße angeben, wie viele Elemente Sie möchten und ob Sie den richtigen Endpunkt einfügen möchten.
Es könnte so aussehen, als hätte NumPy keinen Rundungsfehler bei der Berechnung der Elemente, aber das liegt nur an der unterschiedlichen Anzeigelogik. NumPy schneidet die angezeigte Genauigkeit aggressiver ab als float.__repr__
. Wenn Sie tolist
verwenden, um eine gewöhnliche Liste gewöhnlicher Python-Skalare (und damit die normale float
-Anzeigelogik) zu erhalten, können Sie sehen, dass NumPy auch einen Rundungsfehler aufweist:
Es hat etwas verschiedenen Rundungsfehler erlitten - zum Beispiel in .6 und .7 statt .8 und .9 - weil es auch ein anderes Mittel zur Berechnung der Elemente verwendet, implementiert in < a href="https://github.com/numpy/numpy/blob/1d592c12ca7f9c7f471aa8d20b538c5cb4f2cdce/numpy/core/src/multiarray/arraytypes.c.src#L3556"> fill
Funktion für den relevanten dtype.
Die fill
-Funktionsimplementierung hat den Vorteil, dass start + i*step
verwendet wird, anstatt den Schritt wiederholt hinzuzufügen, wodurch ein Akkumulationsfehler bei jeder Addition vermieden wird. Es hat jedoch den Nachteil, dass es (aus keinem zwingenden Grund, den ich sehen kann) den Schritt aus den ersten beiden Elementen neu berechnet, anstatt den Schritt als Argument zu nehmen, so dass es eine große Genauigkeit in der Stufe verlieren kann / p>
Während arange
den Bereich etwas anders durchläuft, hat es immer noch das Float-Repräsentationsproblem:
Der Druck verbirgt das; wandeln Sie es in eine Liste um, um die blutigen Details zu sehen:
%Vor%oder mit einer anderen Iteration
%Vor% In diesem Fall ist jedes angezeigte Element ein np.float64
, wobei wie im ersten Fall float
steht.
Abgesehen von der unterschiedlichen Darstellung von Listen und Arrays arbeitet NumPys arange
durch Multiplizieren statt wiederholtes Hinzufügen. Es ist eher wie:
Dann ist die Ausgabe vollständig gleich:
%Vor%Bei wiederholter Addition würden Sie Gleitkomma-Rundungsfehler "akkumulieren". Die Multiplikation ist immer noch durch Runden betroffen, aber der Fehler wird nicht akkumuliert.
Wie in den Kommentaren erwähnt, ist es nicht wirklich was passiert. Soweit ich es sehe, ist es mehr wie:
%Vor%Aber nicht sicher, ob das genau richtig ist, weil in NumPy viel mehr los ist.