Warum nimmt die Auflösung von Gleitkommazahlen von einem Ursprung weiter ab?


19

Meine OpenGL-Szene enthält Objekte, die in lächerlich großen Entfernungen vom Ursprung positioniert sind. Wenn ich diese Objekte ansehe und eine Kamera um sie schwenke, drehe oder zoome, zittern sie. Das heißt, die Scheitelpunkte, aus denen sich die Objekte zusammensetzen, scheinen um ein imaginäres 3D-Gitter von Punkten zu rasten. Ich habe gelesen, dass dies ein häufiges Problem ist, da viele Informationen mit Gleitkomma-Genauigkeit gespeichert werden können (was OpenGL und so ziemlich alles andere verwendet). Ich verstehe nicht, warum das passiert.

Bei der Suche nach einer Lösung stieß ich auf die sehr einfache Fehlerbehebung mit „schwebendem Ursprung“, und sie scheint zu funktionieren. Ich transformiere einfach alles so, dass sich meine Objekte in den gleichen relativen Positionen befinden, aber was auch immer meine Kamera betrachtet, ist nahe am Ursprung. Ich habe hier eine Erklärung gefunden: http://floatingorigin.com/ , aber ich konnte ihr nicht folgen.

Könnte jemand erklären, warum das Positionieren meiner Szene sehr weit weg (sagen wir 10 Millionen Einheiten) vom Ursprung zu dem von mir beobachteten unregelmäßigen Verhalten führt? Und warum kann das Problem behoben werden, wenn Sie es in die Nähe des Ursprungs verschieben?


4
Denn wenn nicht, wären sie Festkommazahlen . Tautologische Frage, das.
MSalters

1
Richtig, aber nur, wenn Sie verstehen, was Floating Point wirklich bedeutet.
Kylotan,

Antworten:


26

Dies ist ALLES aufgrund der Art und Weise, wie Gleitkommazahlen in Computern dargestellt werden.

Ganzzahlen werden ziemlich einfach gespeichert. Jede Einheit ist genau "eins" von der "vorherigen", so wie Sie es bei abzählbaren Zahlen erwarten würden.

Bei Gleitkommazahlen ist dies nicht genau der Fall. Stattdessen geben mehrere Bits das EXPONENT an, und der Rest gibt an, was als Mantisse oder Bruchteil bezeichnet wird, der dann vom Exponententeil (implizit 2 ^ exp) MULTIPLIZIERT wird, um das Endergebnis zu erhalten.

Schauen Sie hier für eine visuelle Erklärung der Bits.

Gerade weil dieser Exponent ein tatsächlicher Teil der Bits ist, beginnt die Genauigkeit zu schwinden, sobald die Zahlen größer werden.

Um dies in Aktion zu sehen, lassen Sie uns eine Faux-Floating-Point-Repräsentation durchführen, ohne auf das Wesentliche einzugehen: Nehmen Sie einen kleinen Exponenten wie 2 und führen Sie einige Bruchteile zum Testen durch:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

...etc.

Diese Zahlen wachsen nur bei Exponent 2 nicht sehr weit auseinander. Versuchen wir nun Exponent 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Whoa, großer Unterschied jetzt!

Das Beispiel geht zwar nicht speziell auf die SEHR NÄCHSTE ZÄHLBARE (das wäre der nächste Bruchteil, abhängig von der Anzahl der Bits), zeigt aber den Präzisionsverlust, sobald die Zahlen größer werden. Die "next countable" -Einheit in floats ist sehr klein mit kleinen Exponenten und SEHR groß mit größeren Exponenten, während sie in ganzen Zahlen IMMER 1 ist.

Der Grund, warum die Methode float origin funktioniert, ist, dass sie all diese potenziell großexponentigen Gleitkommazahlen auf kleinexponentig skaliert, sodass die "nächsten Zählwerte" (Genauigkeit) sehr klein und glücklich sein können.


Die Beispiele, die Sie gaben, waren wirklich illustrativ, danke :)
Pris

3
Auf dem richtigen Weg, aber ich wünschte, Sie hätten Beispiele verwendet, die näher an der Wirkungsweise von Fließkommazahlen liegen. Die Mantisse wird nicht zum Exponenten erhoben. es ist Mantisse * 2 ^ Exponent.
Nathan Reed

3
Du hast recht, das wusste ich; Ich weiß nicht, was ich gedacht habe. Bearbeitet meine Antwort.

1
@ScottW Nice edit! +1
Nathan Reed

17

Weil Gleitkommazahlen als Bruch + Exponent + Vorzeichen dargestellt werden und Sie nur eine feste Anzahl von Bits für den Bruchteil haben.

http://en.wikipedia.org/wiki/Single_precision

Wenn Sie immer größere Zahlen erhalten, haben Sie einfach nicht die Bits, um die kleineren Teile darzustellen.


8

Der Klassiker auf diesem Gebiet muss angesprochen werden: Was jeder Informatiker über Gleitkommazahlen wissen sollte .

Das Wesentliche dabei ist jedoch, dass Gleitkommazahlen mit einfacher (doppelter) Genauigkeit nur eine 32-Bit-Binärzahl (64-Bit-Binärzahl) sind, wobei 1 Bit das Vorzeichen darstellt, ein 8-Bit-Exponent (11-Bit-Exponent) der Basis 2 und eine 23-Bit-Bedeutung (52-Bit) (die Klammern sind die Werte für Doubles).

Das heißt, die kleinste positive Zahl, die Sie mit einfacher Genauigkeit darstellen können, ist 0,00000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .

Die nächste positive Zahl ist doppelt so groß wie: 0,00000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , und dann ist die nächste Zahl die Summe der beiden vorherigen 0,0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4,2 - 45 .

Dies nimmt weiterhin um die gleiche konstante Differenz zu, bis: 0,11111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,17549435 x 10 -38 - 0,00000014 x 10 -38 = 1,17549421 x 10 -38

Jetzt haben Sie die normalen Zahlen erreicht (wobei die erste Ziffer in der Bedeutung 1 ist): 1,0000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 und die nächste Zahl ist dann 1,000000000000000001 x 2 -126 = 2 -126 (1 + 2 - 22 ) = 1,17549435 x 1,00000023.


2

Der Grund, warum Gleitkommazahlen weiter vom Ursprung entfernt ungenauer werden, ist, dass eine Gleitkommazahl große Zahlen darstellen kann. Die Art und Weise, wie dies gemacht wird, verleiht ihm den Begriff "Gleitkomma". Es teilt die möglichen Werte auf, die es annehmen kann (was durch seine Bitlänge bestimmt wird), so dass es für jeden Exponenten ungefähr dieselbe Zahl gibt: Bei einem 32-Bit-Float definieren 23 der Bits die Mantisse oder den Signifikanten. So kann es in jedem Exponentenbereich den Wert von 2 ^ 23 verschiedenen Werten annehmen. Einer dieser Exponentenbereiche ist 1-2 [2 ^ 0 bis 2 ^ 1], so dass die Aufteilung des Bereichs 1 bis 2 in 2 ^ 23 verschiedene Werte viel Präzision ermöglicht.

Wenn Sie jedoch den Bereich [2 ^ 10 bis 2 ^ 11] in 2 ^ 23 verschiedene Werte aufteilen, ist der Abstand zwischen den einzelnen Werten viel größer. Wenn dies nicht der Fall wäre, würden 23 Bits nicht ausreichen. Das Ganze ist ein Kompromiss: Sie brauchen unendlich viele Bits, um eine reelle Zahl darzustellen. Wenn Ihre Anwendung so funktioniert, dass Sie bei größeren Werten mit geringerer Genauigkeit davonkommen und von der Möglichkeit profitieren, große Werte tatsächlich darzustellen , verwenden Sie eine Gleitkommadarstellung.


Ich mache hier nur eine Notiz, nachdem ich sie 7 Jahre später noch einmal durchgesehen habe. Meine Zahlen in meinen Beispielen sind nicht besonders gut gewählt. Es gelten aber die Gesamtpunkte.
Steven Lu

1

Es kann etwas schwierig sein, konkrete Beispiele für die Funktionsweise der Gleitkommapräzision anzubieten. Um die anderen Antworten zu ergänzen, hier eine. Angenommen, wir haben eine dezimale Gleitkommazahl mit drei Stellen der Mantisse und einer Stelle des Exponenten:

Mantisse × 10 Exponent

Wenn der Exponent 0 ist, kann jede Ganzzahl im Bereich von 0 bis 999 genau dargestellt werden. Wenn es 1 ist, multiplizieren Sie im Wesentlichen jedes Element dieses Bereichs mit 10, sodass Sie den Bereich 0–9990 erhalten. Jetzt können jedoch nur Vielfache von 10 genau dargestellt werden, da Sie immer noch nur drei Stellen Genauigkeit haben. Wenn der Exponent maximal 9 ist, beträgt die Differenz zwischen jedem Paar darstellbarer ganzer Zahlen eine Milliarde . Sie tauschen buchstäblich Präzision gegen Reichweite.

Bei binären Gleitkommazahlen funktioniert dies genauso: Wenn der Exponent um eins erhöht wird , verdoppelt sich der Bereich , aber die Anzahl der darstellbaren Werte innerhalb dieses Bereichs halbiert sich . Dies gilt auch für gebrochene Zahlen, was natürlich die Ursache Ihres Problems ist.


0

Im Allgemeinen wird die Auflösung schlechter, da die Auflösung mit dem Exponentenwert (2 ** Exponententeil) multipliziert wird.

zur anerkennung von joshs kommentar: das obige war nur, um die antwort in eine kurze aussage zu fassen. Natürlich, wie ich auf http://floatingorigin.com/ angedeutet habe , ist dies erst der Anfang einer Gesamtlösung, und Ihr Programm könnte an mehreren Stellen zittern: in der Präzisions-Pipeline oder in anderen Teilen des Codes .


Dies fügt nichts hinzu, was in anderen Antworten noch nicht vorhanden ist.

Richtig: Ich erkannte, dass ich die Antwort in einer einzigen Zeile beschreiben konnte und dachte, dass jemand eine prägnante Antwort nützlich finden könnte.
Chris Thorne

-1

OpenGL-Tiefenpuffer ist nicht linear . Je weiter Sie fortfahren, desto schlechter ist die Auflösung. Ich empfehle lesen diese . Etwas von dort genommen (12.070):

Zusammenfassend lässt sich sagen, dass die perspektivische Teilung naturgemäß mehr Z-Präzision in der Nähe der Vorderseite des Ansichtsvolumens verursacht als in der Nähe der Rückseite.

Und noch eine (12.040):

Möglicherweise haben Sie Ihre zNear- und zFar-Schnittebenen so konfiguriert, dass Ihre Tiefenpuffergenauigkeit stark eingeschränkt wird. Im Allgemeinen wird dies durch einen Wert in der zNear-Clipping-Ebene verursacht, der zu nahe bei 0,0 liegt. Mit zunehmender Annäherung der zNear-Clipping-Ebene an 0.0 nimmt die effektive Genauigkeit des Tiefenpuffers dramatisch ab. Wenn Sie die zFar-Schnittebene weiter vom Auge entfernen, wirkt sich dies immer negativ auf die Tiefenpuffergenauigkeit aus, ist jedoch nicht so dramatisch wie das Verschieben der zNear-Schnittebene.

Sie sollten also Ihr nahes Clipping-Flugzeug so weit wie möglich und Ihr fernes Flugzeug so weit wie möglich bewegen.


-1: Die Frage bezieht sich auf die Gleitkommagenauigkeit, nicht auf die Genauigkeitsprobleme bei der Darstellung nichtlinearer Tiefenpuffer.
Nathan Reed

Es ist möglich, dass das, was ich sehe, auf Probleme mit der Tiefenpufferung zurückzuführen ist. Ich verwende eine Lib auf OpenGL, um meine Szene anzuzeigen, und gehe davon aus, dass die Kamera, die Ansicht sowie die nahen und fernen Schnittebenen eingerichtet werden, um die Größe und Position der Geometrie (seit dem Betrachter) zu berücksichtigen (Das Tool scheint automatisch eine optimale Ansicht für den Szeneninhalt festzulegen.) Aber ich denke, das ist möglicherweise nicht der Fall - ich werde versuchen, mit den Schnittflächen herumzuspielen und dabei die ursprüngliche Position beizubehalten und zu sehen, was passiert.
Pris

2Nathan Reed: Der Autor schrieb, dass er OpenGL-Szene hat, also dachte ich, es könnte auch dieses Problem sein.
Zacharmarz

Dieses Problem mag ähnlich oder verwandt erscheinen, aber die Tiefenpufferwerte werden definitiv NICHT auf eine Weise gespeichert, die mit Gleitkommazahlen kompatibel ist. Es ist ein Festkommaformat. Aus diesem Grund kann die Antwort irreführend sein.
Steven Lu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.