Rotation ungenau
-
[url] ]http://olli.informatik.uni-oldenburg.de/Grafiti3/grafiti/flow7/page10.html[/url]
Ich habe mit den hier erklärten Formeln eine Funktion zum rotieren von Objekten geschrieben, das funktioniert auch, aber nicht genau!
Meine Objekte werden zwar rotiert, allerdings nicht genau Beispiel: ich rotiere um 180 Grad, aber das Objekt rotiert nicht ganz um 180 Grad sondern vieleicht um 179 oder so, wenn ich eine 360 Grad Drehung durchführe dürfte man die Rotation ja nicht bemerken da das Objekt sich 1 mal um den Nullpunkt gedreht hat, allerdings sieht man bei mir dass es sich so um die 357 Grad gedreht haben muss.Woran liegt das?
-
Zeig mal den Code.
-
Hier das Beispiel: (Rotation um die z Achse)
for(int i = 0; i < anzahl_vertexe; i++) { float tmp(v_array[i].x); v_array[i].x = v_array[i].x * cos(rot) - v_array[i].y * sin(rot); v_array[i].y = v_array[i].y * cos(rot) + tmp * sin(rot); }
Das ist einer SChleife, die mein komplettes Vertex-Array (v_array) durchgeht und alle Koordinaten rotiert, die Variable rot gibt den Winkel an.
-
Der "tmp"-Variablen nach benutzt Du floats. Aber sin und cos sind double-Funktionen. Versuch's mal mit sinf und cosf.
-
das dürfte wohl kaum der grund für eine solch ungenaue rotation sein.
denn, intern (im prozessor bzw. coprozessor, aber letzteren gibt es ja offiziell nicht mehr) werden sowohl double als auch floats in 80bit floats umgewandlet und dann wieder zurück. ausserdem ist ein "sinf(x)" nichts anderes als ein "(float)sin((double)X)" und steht auch genau so in der <math.h> als
"inline float sinf(float _X) {return ((float)sin((double)_X)); }"
welchen wert benutzt du denn für rot bei 360 grad?
6.2f oder 6,283186f ?
man darf beim bogenmaß nicht gerade sparsam mit den nachkommastellen sein, denn zwei stellen hinter dem komma ist "nur" eine genauigkeit von ca. ein paar hundertstel von 360 grad und dass sind ja eben 1-2 grad die dir fehlen! wenn du also willst, dass deine berechnung auf mehrere nachkommastallen im gradmaß genau ist, dann musst du im bogenmass entsprechend mehr kommastellen verwenden also mindestens 5, denn:
360 grad * 0.1 = 36 grad toleranz
360 grad * 0.01 = 3.6 grad toleranz
360 grad * 0.001 = 0.36 grad toleranz
360 grad * 0.0001 = 0.036 grad toleranz
360 grad * 0.00001 = 0.0036 grad toleranz5 stellen hinter dem komma beim bogenmaß ergibt eine ungenauigkeit von ca. 36 tausendstel im gradmaß und 2 stellen hinter dem komma im bogenmaß eine ungenauigkeit von ca. 3.6 im gradmaß!
das problem liegt eigetlich nur daran, dass 360 grad eine gerade zahl ist und PI also 3,1415926535897932384626433832795 selbst auf viele nachkommastellen nicht rund ist! das führt dazu, dass man im bogenmaß immer ungenauigkeiten hat, egal wie genau man die winkel angibt, ausser beim winkel 0!
das einzige was man da machen kann ist, dass man in seinen programmen immer dieselbe zahl für PI nimmt (bzw. für "gerade" winkel, also vielfache von PI) und auf das intervall 2*PI beschränkt und alle werte als divisionsrest von 2*PI nimmt. wenn man dann 2*PI angibt wird 0 draus und dass ist dann eine gerade zahl, selbst wenn PI ungenau ist und nur eine nachkommastelle hat, also 3.1f.
[ Dieser Beitrag wurde am 04.05.2003 um 14:40 Uhr von KXII editiert. ]
-
BTW: Optimieren die heutigen Compiler so eine Schleife eigentlich so, dass sie die cos- und sin-Berechnungen vor die Schleife setzen? ...die brauchen schließlich ne Menge Zeit und ändern sich nicht.
-
das werden sie wohl kaum tun, höchstens unter ganz bestimmten bedingungen, denn wenn sie das machen, dann würden sie ja den code in seiner arbeitsweise verändern. optimierungen dürfen sich NIE auf die arbeitsweise von code auswirken, sondern nur auf die umsetzung in der maschinensprache.
ausserdem sind die heutigen sinus und cosinus funktionenn schon recht schnell geworden. auf einem pentium 4 dürfte so ein befehl wohl kaum mehr als 50 takte dauern, im vergleich zu früher wo dieser bis zu 500 takte gedauert hat. man sollte sich also keine gedanken um solche peanuts machen. der algorithmus entscheidet! nicht die maschinenbefehle.
-
Hui, das wusste ich garnicht, dass sinf und cosf so simpel definiert sind.
Aber eigentlich müsste doch die Genauigkeit ausreichen, um die Zahl Pi einigermaßen ordentlich zu speichern, und eine Ungenauigkeit von 2 bis 3 Grad dürfte da doch nicht bei rauskommen!
@Tom: Kann es sein, dass Du die Rotation nicht direkt um 180° machst, sondern z.B. in 100 Schritten um jeweils 1.8 Grad? Dann wäre es verständlich! Ansonsten steige mal auf double um, dann aber auch bei der "tmp"-Variablen.
[ Dieser Beitrag wurde am 04.05.2003 um 15:09 Uhr von TomasRiker editiert. ]
-
Original erstellt von KXII:
man sollte sich also keine gedanken um solche peanuts machen. der algorithmus entscheidet! nicht die maschinenbefehle.Auch, wenn der gewählte Algorithmus in der Regel wichtiger ist als die Umsetzung des Algorithmus:
Eine Optimierung von diesem Code würde für die Schleife sicherlich eine Beschleunigung um nen Faktor >2 bringen (wenn das der Compiler nicht macht). Da so eine Schleife normalerweise zeitkritisch und zeitintensiv ist, kann so eine Beschleunigung durchaus relevant sein.
-
es hängt davon ab, wie genau TOM selber die zahl PI wählt. vielleicht ist auch seine angabe von 1-2 grad toleranz falsch und es sind nur 0.1-0.2. aber auch das reicht aus um ein objekt bei einer 360 grad drehung "falsch" umzudrehen.
es ist in unserem beispiel egal ob man floats oder doubles verwendet, wo wir doch nur mit sehr kleinen zahlen und kleinen genauigkeiten rechnen. die zahlen die wir hier betrachten liegen im bereich von wenigen 1000. ich habe es garde selber getestet und die berechung oben liefert bei der sechsten nachkommastelle einen unterschied von 2 wenn ich statt floats doubles verwende, und das ist vernachlässigbar. ich verwende dabei für pi "3.1415926535897932384626433832795f".
natürlich sind doubles genauer als floats (aber das ist trivial). die lösung für das problem von tom liegt wo anders. ich vermute bei einer zu kleinen zahl von PI, oder wie du gesagt hast, dass er kleine schritte macht. wenn floats so ungenau wären wie du annimmst, dann dürfte directx eigentlich keine floats verwenden und müsste stattdessen auch doubles verwenden oder nicht?
-
du hast recht. in diesem beispiel und bei heutigen prozessoren wäre ein auslagern der sinus-/cosinusberechungen wirklich von belang und würde eine beschleunigung bringen.
allerdings müsste man sich noch fragen, ob diese variante auch bei RISC-prozessoren schneller wäre, denn soweit ich weiss haben diese alle befehle hardwareimplementiert und benötigen nur einen takt! (und nicht etwa in microcode codierte befehle wie bei CISC) in falle von RISC-prozessoren wäre das auslagern der berechnungen eine verschwendung und würde den code langsamer machen. (allerdings nur für sehr kleine werte von n, bei n->unendlich ist der unterschied 0)
-
RISC = Reduced Instruction Set Computers
Auf RISC-Prozessoren wirst du wohl kein Sinus oder Cosinus als Befehl finden. Ein Sinus oder Cosinus wird hier wohl mit mehreren Maschinencodebefehlen realisiert.
-
hm ich merk grad ihr redet da was von PI, ich hab in der Variable rot bisher immer 360.0f übergeben um einmal um 360° zu drehen, muss ich das etwa erst noch irgendwie umrechenen?
-
AAAAAAAAAAAAAAAAAAAAAAAH
junge, schon mal was grad- und bogenmaß gehört?
die mathefunktionen von c++ arbeiten eigentlich alle (alle die ich kenne) mit winkelangaben im bogenmaß. das bogenmaß ist eine maßeinheit, die winkel in der länge der strecke angibt, die der winkel auf der kreisbahn eines einheitskreises erzeugt, wobei die gesamtlänge der kreisbahn eines einheitskreises 2*PI entspricht!
dann ist also
360 grad = 2PI
180 grad = 1PI
90 grad = 0.5PI
45 grad = 0.25PI...
...
...x grad = x/180*PI
der sinus von 360 grad ist also sin(2*PI), mit PI=3,1415926535897932384626433832795f
[ Dieser Beitrag wurde am 05.05.2003 um 21:32 Uhr von KXII editiert. ]
[ Dieser Beitrag wurde am 05.05.2003 um 21:33 Uhr von KXII editiert. ]
-
Ne hör ich zum ersten mal, danke jetz funktioniert alles.