it-swarm.dev

Was verursacht Gleitkomma-Rundungsfehler?

Mir ist bewusst, dass Gleitkomma-Arithmetik Präzisionsprobleme hat. Normalerweise überwinde ich sie, indem ich zu einer festen Dezimaldarstellung der Zahl wechsle oder einfach den Fehler vernachlässige.

Ich weiß jedoch nicht, was die Ursachen für diese Ungenauigkeit sind. Warum gibt es so viele Rundungsprobleme mit Float-Zahlen?

66
nmat

Dies liegt daran, dass einige Brüche eine sehr große (oder sogar unendliche) Anzahl von Stellen benötigen, um ohne Rundung ausgedrückt zu werden. Dies gilt sowohl für die Dezimalschreibweise als auch für die binäre oder eine andere. Wenn Sie die Anzahl der für Ihre Berechnungen zu verwendenden Dezimalstellen begrenzen würden (und vermeiden würden, Berechnungen in Bruchnotation durchzuführen), müssten Sie sogar einen einfachen Ausdruck auf 1/3 + 1/3 runden. Anstatt 2/3 zu schreiben, müssten Sie 0,33333 + 0,33333 = 0,66666 schreiben, was nicht mit 2/3 identisch ist.

Im Falle eines Computers ist die Anzahl der Stellen durch die technische Natur seines Speichers und seiner CPU-Register begrenzt. Die intern verwendete binäre Notation führt zu weiteren Schwierigkeiten. Computer können Zahlen normalerweise nicht in Bruchschreibweise ausdrücken, obwohl einige Programmiersprachen diese Fähigkeit hinzufügen, wodurch diese Probleme bis zu einem gewissen Grad vermieden werden können.

Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte

84

Rundungsfehler entstehen in erster Linie dadurch, dass die Unendlichkeit aller reellen Zahlen unmöglich durch den endlichen Speicher eines Computers dargestellt werden kann, geschweige denn durch eine winzige Scheibe Speicherplatz wie eine einzelne Gleitkommavariable, so viele gespeicherte Zahlen sind nur Annäherungen an die Zahl, die sie darstellen sollen.

Da es nur eine begrenzte Anzahl von Werten gibt, die nicht eine Näherung sind, und jede Operation zwischen einer Näherung und einer Eine andere Zahl ergibt eine Annäherung, Rundungsfehler sind fast unvermeidlich.

Das Wichtigste ist, zu erkennen, wann sie wahrscheinlich ein Problem verursachen und Schritte zu unternehmen, um die Risiken zu mindern.


Zusätzlich zu David Goldberg ist das Wesentliche Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte (neu veröffentlicht von Sun/Oracle als Anhang zu ihrem Numerical Computation Guide ), der von thorsten , dem - erwähnt wurde. ACCU journal Überladung hat eine exzellente Artikelserie von Richard Harris über den Floating Point Blues.

Die Serie begann mit

Numerisches Rechnen hat viele Fallstricke. Richard Harris sucht nach einer Silberkugel.

Der Drache des numerischen Fehlers wird nicht oft aus seinem Schlaf geweckt, aber wenn er sich unachtsam nähert, fügt er den Berechnungen des unachtsamen Programmierers gelegentlich katastrophalen Schaden zu.

So sehr, dass einige Programmierer, die ihn in den Wäldern der Gleitkomma-Arithmetik nach IEEE 754 zufällig getroffen haben, ihren Kollegen davon abraten, in diesem schönen Land zu reisen.

In dieser Artikelserie werden wir die Welt des numerischen Rechnens untersuchen und Gleitkomma-Arithmetik mit einigen der Techniken kontrastieren, die als sicherer Ersatz dafür vorgeschlagen wurden. Wir werden erfahren, dass das Territorium des Drachen tatsächlich weitreichend ist und dass wir im Allgemeinen vorsichtig sein müssen, wenn wir seine verheerende Aufmerksamkeit fürchten.

Richard erklärt zunächst die Taxonomie von reellen, rationalen, irrationalen, algebraischen und transzendentalen Zahlen. Anschließend erklärt er die IEEE754-Darstellung, bevor er auf Stornierungsfehler und Ausführungsreihenfolgeprobleme eingeht.

Wenn Sie nicht tiefer lesen, haben Sie eine hervorragende Grundlage für die Probleme, die mit Gleitkommazahlen verbunden sind.

Wenn Sie jedoch mehr wissen möchten, fährt er fort

Dann wechselt er zu dem Versuch, Ihnen zu helfen, Ihren Calculus Blues zu heilen.

und zu guter Letzt gibt es

Die gesamte Artikelserie ist einen Blick wert und mit insgesamt 66 Seiten immer noch kleiner als die 77 Seiten des Goldberg-Papiers .

Während diese Serie einen Großteil des gleichen Gebiets abdeckt, fand ich sie eher zugänglich als Goldbergs Papier . Ich fand es auch einfacher, die komplexeren Teile des Papiers zu verstehen, nachdem ich die früheren Artikel von Richards gelesen hatte, und nach diesen frühen Artikeln verzweigt sich Richard in viele interessante Bereiche, die vom Goldberg-Papier nicht berührt werden.


Als also sprach a.k. in Kommentaren erwähnt:

Als Autor dieser Artikel möchte ich erwähnen, dass ich interaktive Versionen davon in meinem Blog erstellt habe www.thusspakeak.com beginnend mit thusspakeak.com/ak/2013/06 .

74
Mark Booth

Nun, thorsten hat das definitive link . Ich würde hinzufügen:

Jede Form der Darstellung weist für eine bestimmte Zahl einen Rundungsfehler auf. Versuchen Sie, 1/3 im IEEE-Gleitkomma oder in Dezimalzahl auszudrücken. Keiner kann es genau machen. Dies geht über die Beantwortung Ihrer Frage hinaus, aber ich habe diese Faustregel erfolgreich angewendet:

  • Speichern Sie vom Benutzer eingegebene Werte in Dezimalzahl (da sie mit ziemlicher Sicherheit in einer Dezimaldarstellung eingegeben wurden - nur sehr wenige Benutzer verwenden Binär- oder Hexadezimalwerte). Auf diese Weise haben Sie immer die genaue vom Benutzer eingegebene Darstellung.
  • Wenn Sie vom Benutzer eingegebene Brüche speichern müssen, speichern Sie den Zähler und den Nenner (auch in Dezimalzahl).
  • Wenn Sie ein System mit mehreren Maßeinheiten für dieselbe Menge (z. B. Celsius/Fahrenheit) haben und der Benutzer beide eingeben kann, speichern Sie den eingegebenen Wert und die eingegebenen Einheiten. Versuchen Sie nicht, zu konvertieren und zu speichern als eine einzelne Darstellung, es sei denn, Sie können dies ohne Verlust an Präzision/Genauigkeit tun. Verwenden Sie in allen Berechnungen die gespeicherten Werte und .
  • Speichern Sie maschinengenerierte Werte im IEEE-Gleitkomma (dies können Zahlen sein, die von einem elektronischen Messgerät wie einem analogen Sensor mit einem A/D-Wandler oder dem ungerundeten Ergebnis einer Berechnung generiert werden). Beachten Sie, dass dies nicht gilt, wenn Sie einen Sensor über eine serielle Verbindung lesen und der Wert bereits in einem Dezimalformat (z. B. 18,2 ° C) angezeigt wird.
  • Speichern Sie vom Benutzer sichtbare Summen usw. in Dezimalzahlen (wie bei einem Kontostand). Runden Sie entsprechend, verwenden Sie diesen Wert jedoch als endgültigen Wert für alle zukünftigen Berechnungen.
12
Scott Whitlock

Was bisher nicht erwähnt worden zu sein scheint, sind die Konzepte eines instabiler Algorithmus und eines schlecht konditioniertes Problem. Ich werde zuerst auf das erstere eingehen, da dies eine häufigere Gefahr für unerfahrene Numeriker zu sein scheint.

Betrachten Sie die Berechnung der Potenzen des (reziproken) Goldenen Schnitts φ=0.61803…; Eine Möglichkeit besteht darin, die Rekursionsformel φ^n=φ^(n-2)-φ^(n-1) zu verwenden, beginnend mit φ^0=1 und φ^1=φ. Wenn Sie diese Rekursion in Ihrer bevorzugten Computerumgebung ausführen und die Ergebnisse mit genau ausgewerteten Leistungen vergleichen, werden Sie eine langsame Erosion signifikanter Zahlen feststellen. Folgendes passiert zum Beispiel in Mathematica:

ph = N[1/GoldenRatio];  
Nest[Append[#1, #1[[-2]] - #1[[-1]]] & , {1, ph}, 50] - ph^Range[0, 51]  
{0., 0., 1.1102230246251565*^-16, -5.551115123125783*^-17, 2.220446049250313*^-16, 
-2.3592239273284576*^-16, 4.85722573273506*^-16, -7.147060721024445*^-16, 
1.2073675392798577*^-15, -1.916869440954372*^-15, 3.1259717037102064*^-15, 
-5.0411064211886014*^-15, 8.16837916750579*^-15, -1.3209051907825398*^-14, 
2.1377864756200182*^-14, -3.458669982359108*^-14, 5.596472721011714*^-14, 
-9.055131861349097*^-14, 1.465160458236081*^-13, -2.370673237795176*^-13, 
3.835834102607072*^-13, -6.206507137114341*^-13, 1.004234127360273*^-12, 
-1.6248848342954435*^-12, 2.6291189633497825*^-12, -4.254003796798193*^-12, 
6.883122762265558*^-12, -1.1137126558640235*^-11, 1.8020249321541067*^-11, 
-2.9157375879969544*^-11, 4.717762520172237*^-11, -7.633500108148015*^-11, 
1.23512626283229*^-10, -1.9984762736468268*^-10, 3.233602536479646*^-10, 
-5.232078810126407*^-10, 8.465681346606119*^-10, -1.3697760156732426*^-9, 
2.216344150333856*^-9, -3.5861201660070964*^-9, 5.802464316340953*^-9, 
-9.388584482348049*^-9, 1.5191048798689004*^-8, -2.457963328103705*^-8, 
3.9770682079726053*^-8, -6.43503153607631*^-8, 1.0412099744048916*^-7, 
-1.6847131280125227*^-7, 2.725923102417414*^-7, -4.4106362304299367*^-7, 
7.136559332847351*^-7, -1.1547195563277288*^-6}

Das angebliche Ergebnis für φ^41 Hat das falsche Vorzeichen, und noch früher haben die berechneten und tatsächlichen Werte für φ^39 Keine gemeinsamen Ziffern (3.484899258054952 * ^ - 9 for the computed version against the true value 7.071019424062048 *^-9). Der Algorithmus ist daher instabil, und man sollte diese Rekursionsformel nicht in ungenauer Arithmetik verwenden. Dies liegt an der inhärenten Natur der Rekursionsformel: Es gibt eine "zerfallende" und "wachsende" Lösung für diese Rekursion, und der Versuch, die "zerfallende" Lösung durch Vorwärtslösung zu berechnen, wenn es eine alternative "wachsende" Lösung gibt, bittet für numerische Trauer. Man sollte daher sicherstellen, dass seine numerischen Algorithmen stabil sind.

Nun zum Konzept eines schlecht konditionierten Problems: Auch wenn es einen stabilen Weg gibt, etwas numerisch zu tun, kann es durchaus sein, dass das Problem, das Sie haben, von Ihrem Algorithmus einfach nicht gelöst werden kann . Dies ist die Schuld des Problems selbst und nicht die Lösungsmethode. Das kanonische Beispiel in der Numerik ist die Lösung linearer Gleichungen mit der sogenannten "Hilbert-Matrix":

Hilbert matrix

Die Matrix ist das kanonische Beispiel einer schlecht konditionierten Matrix: Der Versuch, ein System mit einer großen Hilbert-Matrix zu lösen, kann zu einer ungenauen Lösung führen.

Hier ist eine Mathematica Demonstration: Vergleichen Sie die Ergebnisse der exakten Arithmetik

Table[LinearSolve[HilbertMatrix[n], HilbertMatrix[n].ConstantArray[1, n]], {n, 2, 12}]
{{1, 1}, {1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 
  1}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
   1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 
  1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}

und ungenaue Arithmetik

Table[LinearSolve[N[HilbertMatrix[n]], N[HilbertMatrix[n].ConstantArray[1, n]]], {n, 2, 12}]
{{1., 1.}, {1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1., 1.},  
  {1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1.}, 
  {1., 1., 1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1., 1., 1.},  
  {1., 1., 1., 0.99997, 1.00014, 0.999618, 1.00062, 0.9994, 1.00031, 
  0.999931}, {1., 1., 0.999995, 1.00006, 0.999658, 1.00122, 0.997327, 
  1.00367, 0.996932, 1.00143, 0.999717}, {1., 1., 0.999986, 1.00022, 
  0.998241, 1.00831, 0.975462, 1.0466, 0.94311, 1.04312, 0.981529, 
  1.00342}}

(Wenn Sie es in Mathematica ausprobiert haben, werden Sie einige Fehlermeldungen bemerken, die vor dem Auftreten einer schlechten Konditionierung warnen.)

In beiden Fällen ist es keine Heilung, einfach die Präzision zu erhöhen. es wird nur die unvermeidliche Erosion der Zahlen verzögern.

Dies ist, was Sie konfrontiert sein könnten. Die Lösungen könnten schwierig sein: Zum einen gehen Sie entweder zum Zeichenbrett zurück oder stöbern in Zeitschriften/Büchern/was auch immer, um herauszufinden, ob jemand anderes eine bessere Lösung gefunden hat als Sie; Zum zweiten geben Sie entweder auf oder formulieren Ihr Problem in etwas umsetzbareres um.


Ich werde Sie mit einem Zitat von Dianne O'Leary verlassen:

Das Leben mag uns einige schlecht konditionierte Probleme bereiten, aber es gibt keinen guten Grund, sich mit einem instabilen Algorithmus zufrieden zu geben.

10
user1372

weil Dezimalzahlen der Basis 10 nicht in Basis 2 ausgedrückt werden können

oder mit anderen Worten, 1/10 kann nicht in einen Bruch mit einer Potenz von 2 im Nenner umgewandelt werden (was im Wesentlichen Gleitkommazahlen sind)

9
ratchet freak

In der Mathematik gibt es unendlich viele rationale Zahlen. Eine 32-Bit-Variable kann nur 2 haben32 verschiedene Werte und eine 64-Bit-Variable nur 264 Werte. Daher gibt es unendlich viele rationale Zahlen, die keine genaue Darstellung haben.

Wir könnten Schemata entwickeln, mit denen wir 1/3 oder 1/100 perfekt darstellen können. Es stellt sich heraus, dass dies für viele praktische Zwecke nicht sehr nützlich ist. Es gibt eine große Ausnahme: Im Finanzbereich tauchen häufig Dezimalbrüche auf. Das liegt hauptsächlich daran, dass Finanzen im Wesentlichen eine menschliche Aktivität sind, keine physische.

Daher verwenden wir normalerweise binäre Gleitkommazahlen und runden jeden Wert, der nicht binär dargestellt werden kann. Im Finanzbereich wählen wir manchmal Dezimal-Gleitkommawerte und runden Werte auf den nächsten Dezimalwert.

9
MSalters