Moin Moin,
ich befasse mich z Zt unter anderem mit Vektoren (ich will mich ein
wenig an der Grafikprogrammierung versuchen)
Dazu hab ich ein struct sVektor erschaffen, in dem die Werte für die
einzelnen Dimensionen des Vektors drin stehen. Desweiteren eine
Funktion, die 2 solcher Vektoren addieren kann. Das macht sie
normalerweise auch recht gut. Aber an einer Stelle des Programms meinte
sie, -1 + 1 wäre 4.857..... und nicht 0. Ich schmeiße in die Funktion
Pointer der zu addierenden Vektoren und sie gibt mir das Ergebnis
zurück.
Nachdem das an dieser Stelle nicht funktioniert hat, hab ich das was die
Funktion macht, an der Stelle einfach nochmal hingeschrieben. Trotzdem
noch das falsche Ergebnis.
Dann hab ich mir gedacht, evtl geht irgendwas mit den Pointern schief.
Also hab ich mir neue sVektor erstellt, die Werte des Pointers
reingeladen. Die Werte werden in der Watch korrekt angezeigt. Der X-wert
des einen Vektors ist -1, der des Anderen 1. Ich addiere die beiden
X-werte der Vektoren, es kommt aber 4.857..... raus.
Im Anhang mal Bilder während des Degbuggens, man sieht einmal die Watch
vor dem Schritt, und dann hinterher. Desweiteren noch der Quelltext.
P.S. ich hab die Watch grad mal "breiter" gezogen. Es sind "nur"
4,857....*10^-17, also nur ne winzige Abweichung von null. Aber halt
nicht null..... Ich schreib übrigens in CodeBlocks mit GCC
mit etwas verwirrten Grüßen, Chaos
P.P.S
Hier noch die meiner nach entscheidenden Stellen aus dem Quelltext:
in Grafik_Bildschirm.c
Das ist grad die Variante ohne Pointer, der Rückgabewert ist erst mal
egal, da ich die Werte ja beim debuggen in der Watch ansehe.
und noch die Summierfunktion:
in Transformationen.c
J. T. schrieb:> P.S. ich hab die Watch grad mal "breiter" gezogen. Es sind "nur"> 4,857....*10^-17, also nur ne winzige Abweichung von null. Aber halt> nicht null..... Ich schreib übrigens in CodeBlocks mit GCC
Das ist normal. Beim Rechnen mit float/double muss man mit
Rundungsfehlern rechnen. Wenn du es exakt möchtest musst du mit ints
rechnen aber das eignet sich ja auch nicht gerade für Koordinaten.
Danke erstmal für deine Antwort. Aber wie kann es denn sein, das die
Rundungsfehler unterschiedlich ausfallen? An anderer Stelle sagt er mir
ja exakt null für die selbe Rechnung?
Sebastian V. schrieb:> Wenn du es exakt möchtest musst du mit ints> rechnen aber das eignet sich ja auch nicht gerade für Koordinaten.
Ich hatte mal mit dem Gedanken "Festkomma-Aritmetik" gespielt.
J. T. schrieb:> P.S. ich hab die Watch grad mal "breiter" gezogen. Es sind "nur"> 4,857....*10^-17, also nur ne winzige Abweichung von null.
Sind denn deine Ausgangswerte wirklich exakt 1 und -1? Wenn die durch
vorherige Rundungsfehler irgendwo weit nach dem Komma noch was haben,
kommt als Summe natürlich nicht mehr 0, sondern ein sehr kleiner Wert
raus.
Auch long double kann keine 17 Nachkommastellen auflösen.
PS:
Zitat vom Postingfenster:
"Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Rolf M. schrieb:> Auch long double kann keine 17 Nachkommastellen auflösen.
Und wieder ein, der Floatingpoint nicht verstanden hat.
@TE
Deine 4,857....*10^-17 sollten, wenn du tatsächlich mit 1 und -1
gerechnet hast, kleiner als Epsilon des entsprechenden Compilers ist.
Eine Übersicht gibt es hier:
http://www2.informatik.uni-halle.de/lehre/c/c623.html
Das Problem ist die Darstellung von -1 als binäre Zahl; es handelt sich
um eine Periode. Das führt irgendwann zu Rundungsfehlern.
J. T. schrieb:> Danke erstmal für deine Antwort. Aber wie kann es denn sein, das die> Rundungsfehler unterschiedlich ausfallen? An anderer Stelle sagt er mir> ja exakt null für die selbe Rechnung?
Vielleicht -ffast-math oder -funsafe-math-optimizations aktiviert? Da
könnte je nach Code drumherum mal unterschiedlich gerundet werden.
Allerdings sollte man sich fragen was dagegen spricht wenn irgendwas
10^-17 als Ergebniss rauskommt statt 0. Tests auf Gleichheit mit
Floating Point Zahlen sollte man sowieso vermeiden (aus eben diesem
Grund). Und für normale Anwendungsfälle spielt es meist auch keine
Rolle. Erst recht bei Grafikprogrammierung merkt es kein Mensch wenn
eine Grafik 10^-17 Einheiten von was auch immer falsch positioniert ist.
qwertzuiopü+ schrieb:> eine 4,857....*10^-17 sollten, wenn du tatsächlich mit 1 und -1> gerechnet hast, kleiner als Epsilon des entsprechenden Compilers ist.> Eine Übersicht gibt es hier:
Wow... Visual C++ 1.0, Borland C++ 4.5, GNU C unter DOS. Ja, alles sehr
relevant.
In Visual C++ ist long double schon seit sie auf 32 Bit umgestiegen sind
nicht mehr 80 Bit breit. Somit wird dort der selbe Wert wie für double
gelten.
Rolf M. schrieb:> Sind denn deine Ausgangswerte wirklich exakt 1 und -1?
Sagt zumindest die Watch.
Rolf M. schrieb:> Auch long double kann keine 17 Nachkommastellen auflösen.
Mit Floats kannst du "beliebig"(es gibt schon Grenzen) genau auflösen.
Aber immer nur einen begrenzten Bereich.
qwertzuiopü+ schrieb:> Deine 4,857....*10^-17 sollten, wenn du tatsächlich mit 1 und -1> gerechnet hast, kleiner als Epsilon des entsprechenden Compilers ist.
Hab ich tatsächlich mit gerechnet. Du meinst, der Wert sollte kleiner
SEIN? Sorry normalerweise hack ich nicht auf Rechtschreibung rum, aber
irgendwie ist das mit dem "ist" so sinnentstellend, das ich nachhaken
muss =).
Was genau ist Epsilon?
Erstmal bin ich ja schon froh, das der Wert nur um 4*10^-17 abweicht,
und nicht um 4, das ist ein beruhigend geringfügiger Unterschied :D.
Ich werd mal sehen, ob ich damit leben kann, oder dass doch noch auf
Festkomma umstrick.
J. T. schrieb:> P.S. ich hab die Watch grad mal "breiter" gezogen. Es sind "nur"> 4,857....*10^-17, also nur ne winzige Abweichung von null. Aber halt> nicht null..... Ich schreib übrigens in CodeBlocks mit GCC
Dafür hab ich einen Text:
> Floating point arithmethic is like moving a pile of sand. Every time> you do it, you loose a little sand and pick up a litte dirt.
MfG Klaus
Klaus schrieb:>> Floating point arithmethic is like moving a pile of sand. Every time>> you do it, you loose a little sand and pick up a litte dirt.
Den brachte Karl-Heinz schon, als noch mit floats statt long double
gearbeitet hab, und nach den Rundungsfehlern fragte, da wurde dann (von
jdm anders) in nem Nebensatz "long double" eingeworfen....
Da trat das Problem auf, das mein Würfel beim drehen kontinuierlich um
die Drehachse schrumpfte. Dafür hab ich schon nen Ansatz im
Hinterkopf...
Wolfgang S. schrieb:> Sehr trollig
Den Trollereivorwurf weise ich entschieden zurück!
Das ich unterwegs entdeckt hab, das der Fehler winzig und nicht mehrere
100% ist, hab ich ja shcon im Eingangspost geschrieben.
Wolfgang S. schrieb:> ansonsten nirgendwo> ein Fragezeichen in Sicht
Das Fragezeichen lässt sich eindampfen zu:
Wieso macht er an einer Stelle 0 draus, an dieser aber nicht?
Danke für den Lesestoff
J. T. schrieb:> Klaus schrieb:>>> Floating point arithmethic is like moving a pile of sand. Every time>>> you do it, you loose a little sand and pick up a litte dirt.>> Den brachte Karl-Heinz schon,
Ich wusste nicht mehr, wo ich ihn her hatte. Daher Danke für die
Quellenangabe
MfG Klaus
In einer Renderengine ist es unüblich die Originaldaten eines statischen
Objekts zu ändern, nur um das Objekt zu Drehen, verschieben oder zu
Animieren. Normalerweise geht man Folgendermassen for:
Man hat eine Liste von Objekten. Die Objekte haben Koordinaten für die
Punkte, Indeces um die Punkte zu Dreiecken zu verbinden, eine
Modelviewmatrix für Transformationen, eventuell Keyframes oder
Skeletdaten für Animationen.
Die Koordinaten können entweder alle zusammen in einen grossen
Vertexbuffer kopiert werden, oder einzeln und nacheinander, wobei man
dabei auch gleich zwischen Keyframes usw. interpolieren kann.
Dann multipliziert man Üblicherweise die Modelviewmatrix mit der World-
und Projektionsmatrix und diese dann mit dem dem Vektor. Danach nutzt
man die Index und Vertexdaten um die Geometrie zu zeichnen, dabei
berechnet man die Farbe des Pixels z.B. mit Texturen und mittels der
Baryzentrischen coordinaten interpolierten Texturcordinaten, oder auf
selbige weise interpolierte Farbwerte. Bei der nächsten frame fängt man
wieder bei den noch unveränderten werten an und wendet Transformationen
auf die Jeweiligen matrizen an.
Der Springende Punkt ist, dass man dadurch die Rechenungenauigkeit
reduzieren kann, diese ist aber weiterhin vorhanden. Ausserdem lassen
sich so viele Trigonometrische Funktionen vermeiden, diese sind meist
recht langsam.
Ausserdem würde ich die Grafigkarte ausnutzen, welche meist schon eine
derartige Grafigpipeline eingebaut haben. Ich empfehle OpenGL oder
Direct3D dafür zu verwenden.
Daniel A. schrieb:> Man hat eine Liste von Objekten. Die Objekte haben Koordinaten für die> Punkte, Indeces um die Punkte zu Dreiecken zu verbinden, eine> Modelviewmatrix für Transformationen, eventuell Keyframes oder> Skeletdaten für Animationen.
Keine Modelview-Matrix. Die wird erst in jedem Frame berechnet.
> Die Koordinaten können entweder alle zusammen in einen grossen> Vertexbuffer kopiert werden, oder einzeln und nacheinander, wobei man> dabei auch gleich zwischen Keyframes usw. interpolieren kann.> Dann multipliziert man Üblicherweise die Modelviewmatrix mit der World-
Das, was bei dieser Multiplikation rauskommst, ist erst die
Modelview-Matrix. Deshalb heißt sie ja so - weil sie vom
Modell-Koordinatensystem ins View-Koordinatensytem transformiert.
> und Projektionsmatrix und diese dann mit dem dem Vektor.> Bei der nächsten frame fängt man wieder bei den noch unveränderten werten> an und wendet Transformationen auf die Jeweiligen matrizen an.
Das ist der wesentliche Teil.
J. T. schrieb:> Das Fragezeichen lässt sich eindampfen zu:>> Wieso macht er an einer Stelle 0 draus, an dieser aber nicht?
Keine Ahnung. Das wirst Du vermutlich herausfinden können, wenn Du für
beide Fälle das Programm auf eine geschlossene Formel reduzierst und
beim Vergleich den genannten Lesestoff berücksichtigst. Die Arbeit wirst
Du Dir schon selber machen müssen.
> Danke für den Lesestoff
Zwischenzeitlich schon gelesen?
Einfachere Variante:
https://docs.python.org/2/tutorial/floatingpoint.html
qwertzuiopü+ schrieb:> Das Problem ist die Darstellung von -1 als binäre Zahl; es handelt sich> um eine Periode. Das führt irgendwann zu Rundungsfehlern.
Hä? Bei -1 ist überhaupt nix periodisch. Bis auf das Vorzeichenbit hat
das exakt den selben Wert wie 1. Für jemanden, der so auf den Putz haut
wie du, schreibst du einen beeinduckenden Unsinn.
Du hast das vermutlich mit dem Klassiker 10 * 0.1 verwechselt, wo nicht
1 rauskommt, da 0.1 im Dualsystem eine periodische Zahl ist.
qwertzuiopü+ schrieb:> Das Problem ist die Darstellung von -1 als binäre Zahl; es handelt sich> um eine Periode.
Nö. -1 ist eine glatte Zweierpotenz (eben 2^0) mit negativem Vorzeichen.
Nachdem ich nun einiges geändert habe, kann ich mit den Rundungsfehlern
leben.
Ich lasse nun nicht mehr jedesmal die Punkte rotieren, sondern lasse die
Punkte am am selben Fleck, und habe einen Rotationsakkumulator
eingeführt. Dort werden die Rotationswerte einfach aufsummiert und die
Rotationsfunktions wird dann jedesmal nur einmal mit dem Rotationsakku
durchgeführt. So habe ich jedesmal nur den einen kleinen Rundungsfehler
und er wird nicht mitgeschleppt.
Das klappt nun auch nach mehreren Stunden drehen lassen ohne Verzerrung.