Warum verlieren einige Zahlen an Genauigkeit, wenn sie als Gleitkommazahlen gespeichert werden?
Zum Beispiel kann die Dezimalzahl 9.2
genau als Verhältnis von zwei Dezimalzahlen ( 92/10
) ausgedrückt werden, die beide genau in binär ( 0b1011100/0b1010
) ausgedrückt werden können . Das gleiche Verhältnis, das als Gleitkommazahl gespeichert ist, ist jedoch niemals genau gleich 9.2
:
32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875
Wie kann eine so scheinbar einfache Zahl "zu groß" sein, um sie in 64 Bit Speicher auszudrücken ?
floating-point
language-agnostic
precision
mhlester
quelle
quelle
Antworten:
In den meisten Programmiersprachen werden Gleitkommazahlen ähnlich wie in der wissenschaftlichen Notation dargestellt : mit einem Exponenten und einer Mantisse (auch als Signifikand bezeichnet). Eine sehr einfache Zahl
9.2
ist beispielsweise dieser Bruchteil:Wo der Exponent ist
-49
und die Mantisse ist5179139571476070
. Der Grund, warum es unmöglich ist, einige Dezimalzahlen auf diese Weise darzustellen, ist, dass sowohl der Exponent als auch die Mantisse ganze Zahlen sein müssen. Mit anderen Worten, alle Floats müssen eine ganze Zahl multipliziert mit einer ganzzahligen Potenz von 2 sein .9.2
kann einfach sein92/10
, aber 10 kann nicht als 2 n ausgedrückt werden, wenn n auf ganzzahlige Werte beschränkt ist.Daten anzeigen
Zunächst einige Funktionen, um die Komponenten zu sehen , aus denen ein 32- und 64-Bit besteht
float
. Beschönigen Sie diese, wenn Sie sich nur für die Ausgabe interessieren (Beispiel in Python):Hinter dieser Funktion steckt eine Menge Komplexität, und es wäre ziemlich tangential zu erklären, aber wenn Sie interessiert sind, ist die Struktur die wichtige Ressource für unsere Zwecke .
Python
float
ist eine 64-Bit-Zahl mit doppelter Genauigkeit. In anderen Sprachen wie C, C ++, Java und C # hat die doppelte Genauigkeit einen separaten Typdouble
, der häufig als 64-Bit implementiert wird.Wenn wir diese Funktion mit unserem Beispiel aufrufen, erhalten
9.2
wir Folgendes:Daten interpretieren
Sie werden sehen, dass ich den Rückgabewert in drei Komponenten aufgeteilt habe. Diese Komponenten sind:
Zeichen
Das Vorzeichen wird in der ersten Komponente als einzelnes Bit gespeichert. Es ist leicht zu erklären:
0
bedeutet, dass der Float eine positive Zahl ist;1
bedeutet, dass es negativ ist. Weil9.2
positiv ist, ist unser Vorzeichenwert0
.Exponent
Der Exponent wird in der mittleren Komponente als 11 Bit gespeichert. In unserem Fall
0b10000000010
. In Dezimalzahlen entspricht dies dem Wert1026
. Eine Besonderheit dieser Komponente ist, dass Sie eine Zahl von 2 ( Anzahl der Bits) - 1 - 1 subtrahieren müssen , um den wahren Exponenten zu erhalten. In unserem Fall bedeutet dies Subtrahieren0b1111111111
(Dezimalzahl1023
), um den wahren Exponenten0b00000000011
(Dezimalzahl 3) zu erhalten.Mantisse
Die Mantisse ist in der dritten Komponente als 52 Bit gespeichert. Diese Komponente hat jedoch auch eine Besonderheit. Um diese Eigenart zu verstehen, betrachten Sie eine Zahl in wissenschaftlicher Notation wie folgt:
Die Mantisse wäre die
6.0221413
. Denken Sie daran, dass die Mantisse in wissenschaftlicher Notation immer mit einer einzelnen Ziffer ungleich Null beginnt. Das gleiche gilt für Binär, außer dass Binär nur zwei Ziffern hat:0
und1
. Die binäre Mantisse beginnt also immer mit1
! Wenn ein Float gespeichert ist, wird die1
Vorderseite der binären Mantisse weggelassen, um Platz zu sparen. Wir müssen es wieder vor unser drittes Element setzen, um die wahre Mantisse zu erhalten:Dies beinhaltet mehr als nur eine einfache Addition, da die in unserer dritten Komponente gespeicherten Bits tatsächlich den Bruchteil der Mantisse rechts vom Radixpunkt darstellen .
Beim Umgang mit Dezimalzahlen "verschieben wir den Dezimalpunkt" durch Multiplizieren oder Dividieren mit Potenzen von 10. In Binärzahlen können wir dasselbe tun, indem wir mit Potenzen von 2 multiplizieren oder dividieren. Da unser drittes Element 52 Bits hat, teilen wir es um 2 52 , um es 52 Stellen nach rechts zu bewegen:
In Dezimalschreibweise entspricht dies dem Teilen
675539944105574
durch4503599627370496
, um zu erhalten0.1499999999999999
. (Dies ist ein Beispiel für ein Verhältnis, das genau in Binärform, aber nur ungefähr in Dezimalzahl ausgedrückt werden kann . Weitere Informationen finden Sie unter: 675539944105574/4503599627370496 .)Nachdem wir die dritte Komponente in eine Bruchzahl umgewandelt haben,
1
ergibt das Hinzufügen die wahre Mantisse.Komponenten neu zusammenfassen
0
für positiv,1
für negativ1
, um die wahre Mantisse zu erhaltenBerechnung der Anzahl
Wenn wir alle drei Teile zusammenfügen, erhalten wir diese Binärzahl:
Was wir dann von binär nach dezimal konvertieren können:
Und multiplizieren Sie, um die endgültige Darstellung der Zahl anzuzeigen, mit der wir begonnen haben (
9.2
), nachdem sie als Gleitkommawert gespeichert wurde:Darstellen als Bruch
9.2
Nachdem wir die Zahl erstellt haben, ist es möglich, sie in einen einfachen Bruch zu rekonstruieren:
Verschieben Sie die Mantisse auf eine ganze Zahl:
In Dezimalzahl konvertieren:
Subtrahieren Sie den Exponenten:
Negativen Exponenten in Division umwandeln:
Exponent multiplizieren:
Was gleich ist:
9.5
Sie können bereits sehen, dass die Mantisse nur 4-stellig ist, gefolgt von einer ganzen Reihe von Nullen. Aber lassen Sie uns durch die Schritte gehen.
Stellen Sie die binäre wissenschaftliche Notation zusammen:
Verschieben Sie den Dezimalpunkt:
Subtrahieren Sie den Exponenten:
Binär bis dezimal:
Negativer Exponent zur Teilung:
Exponent multiplizieren:
Gleich:
Weiterführende Literatur
quelle
Dies ist keine vollständige Antwort ( Mhlester hat bereits viele gute Gründe abgedeckt, die ich nicht duplizieren werde), aber ich möchte betonen, wie sehr die Darstellung einer Zahl von der Basis abhängt, in der Sie arbeiten.
Betrachten Sie den Bruch 2/3
In der guten alten Basis 10 schreiben wir es normalerweise so etwas wie
Wenn wir uns diese Darstellungen ansehen, neigen wir dazu, jede von ihnen mit dem Bruch 2/3 zu verknüpfen, obwohl nur die erste Darstellung mathematisch gleich dem Bruch ist. Die zweite und dritte Darstellung / Annäherung weisen einen Fehler in der Größenordnung von 0,001 auf, der tatsächlich viel schlimmer ist als der Fehler zwischen 9,2 und 9,1999999999999993. Tatsächlich ist die zweite Darstellung nicht einmal richtig gerundet! Trotzdem haben wir kein Problem mit 0,666 als Annäherung an die Zahl 2/3, so dass wir eigentlich kein Problem damit haben sollten, wie 9,2 in den meisten Programmen angenähert wird . (Ja, in einigen Programmen ist es wichtig.)
Zahlenbasen
Hier sind also die Zahlenbasen entscheidend. Wenn wir versuchen würden, 2/3 in Basis 3 darzustellen, dann
Mit anderen Worten, wir haben eine exakte, endliche Darstellung für dieselbe Zahl, indem wir die Basis wechseln! Das Mitnehmen ist, dass, obwohl Sie jede Zahl in jede Basis umwandeln können, alle rationalen Zahlen in einigen Basen exakte endliche Darstellungen haben, in anderen jedoch nicht .
Um diesen Punkt nach Hause zu fahren, schauen wir uns 1/2 an. Es könnte Sie überraschen, dass diese vollkommen einfache Zahl zwar eine genaue Darstellung in Basis 10 und 2 hat, jedoch eine wiederholte Darstellung in Basis 3 erfordert.
Warum sind Gleitkommazahlen ungenau?
Weil sie oft Rationalen approximieren, die in Basis 2 nicht endlich dargestellt werden können (die Ziffern wiederholen sich), und im Allgemeinen approximieren sie reelle (möglicherweise irrationale) Zahlen, die in keiner Basis in endlich vielen Ziffern darstellbar sind.
quelle
1/3
genau wie Basis 10 perfekt für1/10
. Keine der Fraktionen funktioniert in Base-2N
oder ein Vielfaches davon ist.π
usw. aufheben.Obwohl alle anderen Antworten gut sind, fehlt noch eines:
Es ist unmöglich , irrationale Zahlen darzustellen (zB π,
sqrt(2)
,log(3)
, etc.) genau!Und deshalb werden sie eigentlich irrational genannt. Keine Menge an Bitspeicher auf der Welt würde ausreichen, um auch nur einen von ihnen aufzunehmen. Nur symbolische Arithmetik kann ihre Präzision bewahren.
Wenn Sie jedoch Ihre mathematischen Anforderungen auf rationale Zahlen beschränken würden, wäre nur das Problem der Präzision beherrschbar. Sie müssten ein Paar (möglicherweise sehr große) Ganzzahlen speichern
a
undb
die durch den Bruch dargestellte Zahl enthaltena/b
. Alle Ihre Arithmetik müsste auf Brüchen ausgeführt werden, genau wie in der Highschool-Mathematik (za/b * c/d = ac/bd
. B. ).Aber natürlich würden Sie noch in die gleiche Art von Schwierigkeiten geraten , wenn
pi
,sqrt
,log
,sin
usw. beteiligt sind.TL; DR
Für hardwarebeschleunigte Arithmetik kann nur eine begrenzte Anzahl rationaler Zahlen dargestellt werden. Jede nicht darstellbare Zahl wird angenähert. Einige Zahlen (dh irrational) können unabhängig vom System niemals dargestellt werden.
quelle
Es gibt unendlich viele reelle Zahlen (so viele, dass man sie nicht aufzählen kann), und es gibt unendlich viele rationale Zahlen (es ist möglich, sie aufzuzählen).
Die Gleitkomma-Darstellung ist endlich (wie alles in einem Computer), so dass es unvermeidlich ist, viele, viele, viele Zahlen darzustellen. Insbesondere können Sie mit 64 Bit nur zwischen 18.446.744.073.709.551.616 verschiedenen Werten unterscheiden (was im Vergleich zu unendlich nichts ist). Mit der Standardkonvention gehört 9.2 nicht dazu. Diejenigen, die können, haben für einige ganze Zahlen m und e die Form m.2 ^ e.
Sie könnten sich ein anderes Nummerierungssystem einfallen lassen, beispielsweise 10, bei dem 9.2 eine genaue Darstellung haben würde. Aber andere Zahlen, sagen wir 1/3, wären immer noch unmöglich darzustellen.
Beachten Sie auch, dass Gleitkommazahlen mit doppelter Genauigkeit äußerst genau sind. Sie können eine beliebige Zahl in einem sehr weiten Bereich mit bis zu 15 exakten Ziffern darstellen. Für Berechnungen des täglichen Lebens sind 4 oder 5 Ziffern mehr als ausreichend. Sie werden diese 15 nie wirklich brauchen, es sei denn, Sie möchten jede Millisekunde Ihres Lebens zählen.
quelle
Gleitkommazahlen sind (leicht vereinfacht) ein Positionsnummerierungssystem mit einer begrenzten Anzahl von Ziffern und einem beweglichen Radixpunkt.
Ein Bruch kann nur dann exakt mit einer endlichen Anzahl von Ziffern in einem Positionsnummerierungssystem ausgedrückt werden, wenn die Primfaktoren des Nenners (wenn der Bruch in seinen niedrigsten Ausdrücken ausgedrückt wird) Faktoren der Basis sind.
Die Primfaktoren von 10 sind 5 und 2, daher können wir in Basis 10 jeden Bruchteil der Form a / (2 b 5 c ) darstellen.
Andererseits ist der einzige Primfaktor von 2 2, so dass wir in Basis 2 nur Brüche der Form a / (2 b ) darstellen können.
Weil es ein einfaches Format ist und für die meisten Zwecke ausreichend genau ist. Grundsätzlich der gleiche Grund, warum Wissenschaftler "wissenschaftliche Notation" verwenden und ihre Ergebnisse bei jedem Schritt auf eine angemessene Anzahl von Stellen runden.
Es wäre sicherlich möglich, ein Bruchformat mit (zum Beispiel) einem 32-Bit-Zähler und einem 32-Bit-Nenner zu definieren. Es wäre in der Lage, Zahlen darzustellen, die IEEE-Gleitkommazahlen mit doppelter Genauigkeit nicht darstellen könnten, aber es gäbe auch viele Zahlen, die in Gleitkommazahlen mit doppelter Genauigkeit dargestellt werden könnten, die in einem solchen Bruchformat mit fester Größe nicht dargestellt werden könnten.
Das große Problem ist jedoch, dass ein solches Format schwierig zu berechnen ist. Aus zwei Gründen.
Einige Sprachen bieten Brucharten an, aber normalerweise tun sie dies in Kombination mit willkürlicher Genauigkeit. Dadurch müssen Sie sich keine Gedanken über die Annäherung von Brüchen machen, aber es entsteht ein eigenes Problem, wenn eine Zahl eine große Anzahl von Berechnungsschritten mit der Größe des Nenners und durchläuft daher kann der für die Fraktion benötigte Speicher explodieren.
Einige Sprachen bieten auch dezimale Gleitkommatypen an. Diese werden hauptsächlich in Szenarien verwendet, in denen es wichtig ist, dass die Ergebnisse, die der Computer erhält, mit bereits vorhandenen Rundungsregeln übereinstimmen, die für Menschen geschrieben wurden (hauptsächlich Finanzberechnungen). Diese sind etwas schwieriger zu bearbeiten als binäre Gleitkommazahlen, aber das größte Problem ist, dass die meisten Computer keine Hardwareunterstützung für sie anbieten.
quelle
Versuche dies
'
decimalValue
' ist Ihr zu konvertierender Wert.quelle