Wie stelle ich auf portable Weise sicher, dass die folgende Berechnung garantiert als Floatingpoint Operation ausgeführt wird und erst am Schluss wieder auf Integer konvertiert wird? (zwecks korrekter Rundung) uint16_t va = 64000; uint16_t vb = 0; vb = (uint_16_t)(4000000 / va - 0.5) //reicht das? vb = (uint_16_t)(4000000.0 / va - 0.5) //oder so? vb = (uint_16_t)((float)4000000 / va - 0.5) //oder sogar so? Sind die drei Varianten identisch?
Peter schrieb: > Sind die drei Varianten identisch? Nein, die erste funktioniert nicht, und die zweite ist äquivalent zu vb = (uint16_t)((double)4000000 / va - 0.5).
Ok, vielen Dank! Da mir "float"-Genauigkeit genügt, werde ich somit die 3. Variante benutzen.
warum willst du unbedingt das als float rechnen, wegen dem Runden? Oder ist das ganze nur ein einfaches Beispiel? Denn hier kann man die selbe genauigkeit auch mit int erreichen.
Ja, wegen dem runden. Das war nur ein Zahlenbeispiel, va ist in der Applikation variabel!
..und die floatrechnung "kostet" nichts, ich habe da einen MPC8314 mit einem PowerPC e300c3 Core, der hat eine FPU intus!
Peter schrieb: > Ja, wegen dem runden. dann rechne doch anders: vb = (4000000+(va/2)) / va; (so in der art - hoffentlich ist kein fehler drin)
Peter schrieb: > ..und die floatrechnung "kostet" nichts, ich habe da einen MPC8314 mit > einem PowerPC e300c3 Core, der hat eine FPU intus! Dann solltest du aber den Compiler entscheiden lassen, ob er float oder double nehmen will. Würde mich nicht wundern, wenn float langsamer als double rechnet. Wenn dir Floating Point nichts kostet, dann willst du nämlich um float einen Bogen machen, wie der Teufel ums Weihwasser.
>dann rechne doch anders: >vb = (4000000+(va/2)) / va; Ob das wirklich schneller ist? >Dann solltest du aber den Compiler entscheiden lassen, ob er float oder >double nehmen will. Wie lasse ich das den Compiler entscheiden? >Würde mich nicht wundern, wenn float langsamer als double rechnet. Ich gehe schon davon aus, das ein float (4Byte) schneller ist als ein double (8Byte), ist nämlich ein 32Bit System, kein 64Bit! >Wenn dir Floating Point nichts kostet, dann willst du nämlich um float >einen Bogen machen, wie der Teufel ums Weihwasser. Häää...? Was willst Du damit sagen?
Peter schrieb: > Ob das wirklich schneller ist? ich denk schon, immerhin fehlt die ganze float int wandlung weg.
Peter schrieb: >>Dann solltest du aber den Compiler entscheiden lassen, ob er float oder >>double nehmen will. > Wie lasse ich das den Compiler entscheiden? vb = (uint_16_t)(4000000.0 / va - 0.5) >>Würde mich nicht wundern, wenn float langsamer als double rechnet. > Ich gehe schon davon aus, das ein float (4Byte) schneller ist als ein > double (8Byte), ist nämlich ein 32Bit System, kein 64Bit! Das hat damit nichts zu tun. Es kommt drauf an, mit welcher Bitbreite die FPU rechnet. Bei den Intel FPU, die zb mit dem 486 aufkamen (und dann im Pentium in die CPU integriert wurden), rechnet die FPU grundsätzlich mit 80 Bit. Lässt du den Compiler schalten und walten, dann lädt er dir die FPU mit den Werten, lässt die Berechnung machen und die FPU dann alles auf int runterbrechen. Zwingst du ihm zwischendurch ein float auf, dann muss der Compiler das 80 Bit Ergebnis zwischendurch erst mal auf 32 Bit runterkonvertieren, um deine Float-Vorgabe zu erfüllen, und dann erst dieses Zwischenergebnis in int konvertieren. Die eigentliche Floating Point Berechnung findet aber immer mit 80 Bit Floating Point Zahlen statt. Das muss jetzt bei deiner FPU nicht so sein, ist aber wahrscheinlich ähnlich, da keine Chip-Schmiede in eine FPU 2 komplett gleiche Rechenwerke nur mit unterschiedlichen Bitbreiten einbauen wird. >>Wenn dir Floating Point nichts kostet, dann willst du nämlich um float >>einen Bogen machen, wie der Teufel ums Weihwasser. > Häää...? Was willst Du damit sagen? Das du 'float' nicht benutzen willst, weil dich nämlich die Auflösung umbringt. Mit Float hast du gerade mal 5 bis 6 signifikante Stellen und das ist nicht viel. Es gibt nur einen einzigen Grund, warum man float nehmen will: Wenn man Millionen davon im Speicher speichern muss. Aber abgesehen davon gibt es keinen Grund float dem double vorzuziehen. Und schon gar nicht wenn man damit ein wenig rumrechnen muss.
Peter schrieb: > Ich gehe schon davon aus, das ein float (4Byte) schneller ist als ein > double (8Byte), ist nämlich ein 32Bit System, kein 64Bit! Und was ist die native size der FPU? Peter II schrieb: > ich denk schon, immerhin fehlt die ganze float int wandlung weg. Wenn der Controller die Integerdivision in der Bibliothek machen muss, Gleitkomma aber in der FPU, dann stimmt deine Annahme nicht mehr. Selbst, wenn er eine Integerdivision in Hardware hat, wird der Unterschied zwischen beiden vernachlässigbar sein, da die Umwandlung float/int typischerweise auch in der FPU und damit recht schnell passiert. Das konnte meiner Erinnerung nach schon ein i8087.
>vb = (uint_16_t)(4000000.0 / va - 0.5)
Diese zweite Variante genügt: da der Divident (4000000.0) eine
Fließkommakonstante ist, wird der Divisor vor der Operation implizit
ebenfalls in einen Fließkommawert konvertiert. Dann wird die Division
durchgeführt, 0.5 subtrahiert und dann wegen des explititen casts
(uint16_t) in eine Ganzzahl konvertiert. Beachte Dabei aber bitte, dass
explizite casts nicht runden im mathematischen Sinn, sondern nur die
Nachkommastellen "abschneiden".
Jörg Wunsch schrieb: > Wenn der Controller die Integerdivision in der Bibliothek machen > muss, Gleitkomma aber in der FPU, dann stimmt deine Annahme nicht > mehr. Selbst, wenn er eine Integerdivision in Hardware hat, wird > der Unterschied zwischen beiden vernachlässigbar sein, da die > Umwandlung float/int typischerweise auch in der FPU und damit > recht schnell passiert. gibt es wirklich eine CPU die eine FPU hat aber keine hardware division? Da es sich hierbei eh nur um ein paar takte handelt ist klar das man da nicht sehr viel gut machen kann. Aber ich meide aus Prinzip float/double wenn ich kann und auch am ende das gleiche rauskommt.
High Performer schrieb: > Beachte Dabei aber bitte, dass > explizite casts nicht runden im mathematischen Sinn, sondern nur die > Nachkommastellen "abschneiden". aus dem grund rechnet er ja -0.5 dann ist es wieder ein runden.
Peter II schrieb: > Aber ich meide aus Prinzip float/double > wenn ich kann und auch am ende das gleiche rauskommt. Ja, eben. Gegen dieses "ich bin aus Prinzip gegen float/double" habe ich persönlich was, da diese Abneigung in aller Regel völlig irrational und technisch unbegründet ist. Selbst auf einem simplen AVR ist eine Gleitkommarechnung meiner Meinung nach in vielen Fällen sehr viel sinnvoller, als dass man sich erstmal um Wertebereiche, Operandenreihenfolgen und dergleichen den Kopf zerbricht (und dann möglicherweise trotzdem noch was verhaun hat und in Überlaufprobleme oder sowas rennt), nur um auf-Teufel-komm-raus ein paar Gleitkommarechnungen zu vermeiden, obwohl der Controller die Ressourcen dafür locker parat hätte. Für viele ingenieurtechnische Probleme sind Gleitkommazahlen nun einmal die am besten passende Ausdrucksweise. Die Umstellung derartiger Rechnungen auf Ganzzahl kann man dann unter "Optimierung" verbuchen, getreu dem Motto: "Never start optimizing before you have profiled it."
Jörg Wunsch schrieb: > Die Umstellung derartiger Rechnungen auf Ganzzahl kann man dann unter > "Optimierung" verbuchen, getreu dem Motto: "Never start optimizing > before you have profiled it." dafür war es bei dem Beispiel eh schon zu spät. Entweder schreibt man vb = round((float)4000000 / va); dann ist es genau was man auch haben willt, wenn man es schon umstellt kann man gleich auf float verzichten. Und ja ich mach mir vorher gedanken über mögliche optmierungen, denn teilweise beieinflussen sie die Programmlogik. Ich habe leider keine gelegenheit auf verschienden CPUs zu testen, ich bin aber immer noch überzeugt das die int lösung die schnellste ist. Und wenn es nur einen Takt ist.
Dieser PowerPC Prozessor enthält eine sehr schnelle 64-Bit FPU, bei der nur die Division in "float" deutlich schneller ist als in "double". Bei solchen Prozessoren sollte man Rechnungen, die teilweise mit Fliesskommawerten durchgeführt werden, so wenig Konvertierungen zwischen Fliesskommadarstellung und Integerdarstellung wie möglich durchführen. Man sollte also Rechnungen entweder konsequent im Fliesskommaformat durchführen, oder garnicht. Denn jede Übertragung von Werten zwischen den verschiedenen execution domains bedeutet Aufwand, u.U. sogar ein Mehrfaches einer Rechenoperation.
>aus dem grund rechnet er ja -0.5 dann ist es wieder ein runden.
Dachte ich auch kurz, aber: nein, es wird kein Runden draus. Er müsste
0,5 addieren, nicht subtrahieren. Da er ja nicht geschrieben hat, was
die Berechnung darstellen soll, hatte ich die -0,5 einfach mal nicht
kommentiert.
High Performer schrieb: > Dachte ich auch kurz, aber: nein, es wird kein Runden draus. dann schein er wohl einen fehler gemacht zu haben, denn er schreibt: > Ja, wegen dem runden.
genauer: 2 Fehler, denn es heißt wegen des Rundens :-)
Karl Heinz Buchegger schrieb: > Das du 'float' nicht benutzen willst, weil dich nämlich die Auflösung > umbringt. Mit Float hast du gerade mal 5 bis 6 signifikante Stellen und > das ist nicht viel. Aber nur, wenn die FPU double-precision kann - z.B. der STM32F4 (ein Cortex-M4F) kann nur single-precision
Jörg Wunsch schrieb: > mehr. Selbst, wenn er eine Integerdivision in Hardware hat, wird > der Unterschied zwischen beiden vernachlässigbar sein, da die > Umwandlung float/int typischerweise auch in der FPU und damit > recht schnell passiert. Das konnte meiner Erinnerung nach schon > ein i8087. Bei PC-Prozessoren ist die Konvertierung von Fliesskommadaten zu Integers über x87 Befehle ziemlich aufwendig, da der Wert per FPU-Befehl als Integer gespeichert und anschliessend per non-FPU Befehl wieder geladen werden muss (oder umgekehrt). Ein direkter Datenpfad bestand im ursprünglichen Design nicht. Zwar gibt es SSE Befehle, die ohne sichtbare Zwischenschaltung des Speichers auskommen, aber je nach Implementierung kann der Schein trügen, wie man an den Laufzeiten erkennt. Bei AMDs Prozessoren sind die execution domains so klar voneinander abgetrennt und unterschiedlich strukturiert, dass es intern trotzdem praktisch aufs Gleiche rausläuft wie bei einem Koprozessor. Bei den PowerPCs dürfte das nicht wesentlich anders aussehen, denn die Historie dieser ISA ist grob ähnlich. Diese Architektur wurde anfangs in mehreren Chips implementiert und darauf optimiert. Sie besitzt daher 3 klar voneinander getrennte Befehlsklassen und Registersets, für Integer-, Fliesskomma- und Branch-Unit. Bezogen auf die hier betrachtete Operation bin ich ziemlich sicher, dass man mit einer reinen Integer-Rechnung deutlich besser fährt, als mit einer Div/Add Operation via FPU und allem anderen als Integerrechnung.
Peter II schrieb: > Peter schrieb: >> Ob das wirklich schneller ist? > > ich denk schon, immerhin fehlt die ganze float int wandlung weg. Beim Software-Renderer von Quake wurde beim Rendern der Texturen eine Perspektivkorrektur durchgeführt (beinhaltet primär eine Division). Diese wurde statt in Integer auf der FPU durchgeführt und war dadurch quasi kostenlos. Der Grund: Die FPU kann parallel zur CPU rechnen, wodurch die Divison gleichzeitig mit den sonstigen Berechnungen der CPU ausgeführt werden konnte und somit keine zusätzliche Zeit in Anspruch genommen hat. Zugegebenermaßen war dieser Code aber auch von Hand in Assembler geschrieben, und die Instruktionen waren genau so angeordnet, daß man diesen Umstand möglichst gut nutzen konnte.
Rolf Magnus schrieb: > Zugegebenermaßen war dieser Code aber auch von Hand in > Assembler geschrieben, und die Instruktionen waren genau so angeordnet, > daß man diesen Umstand möglichst gut nutzen konnte. und vermutlich wurde auch der wert dann weiter als float/double behandelt. Hier will er aber vorher und nachher ein int haben.
Jep, wenn man genug Takte zwischen die FPU-Divisionssequenz und die davon abhängigen Integer-Operationen einschieben kann, dann kann sich das lohnen.
>dann schein er wohl einen fehler gemacht zu haben, denn er schreibt: >> Ja, wegen dem runden. >genauer: 2 Fehler, denn es heißt wegen des Rundens :-) Die Rechnung ist schon korrekt, die Formel lautet nämlich: vb = runden((4000000 / va) - 1) daraus wird: vb = int((4000000 / va) - 0.5) Ich verwende nun doch die Integer Berechnung, ich denke dies ist sicher nicht langsamer als die Berechnung über float oder double. vb = (4000000-(va/2)) / va;
Peter schrieb: > Ich verwende nun doch die Integer Berechnung, ich denke dies ist sicher > nicht langsamer als die Berechnung über float oder double. du könntest uns ja mal den asm code für beide varienten posten
Noch was: Ich denke ich muss bei einem 32 Bitter die Konstante nicht mit einem Long-Attribute versehen? vb = int((4000000UL / va) - 0.5)
Das musst du auch sonst nicht. Eine dezimale lexikalische Konstante kriegt automtisch den kleinsten Integer Typ mit Vorzeichen in den sie reinpasst, mindestens int.
>vb = int((4000000UL / va) - 0.5)
Das wird nicht funktionieren, da (4000000UL / va) eine Integerdivision
darstellt. Das wolltest Du doch gerade vermeiden, oder nicht?
High Performer schrieb: > as wird nicht funktionieren, da (4000000UL / va) eine Integerdivision > darstellt. Das wolltest Du doch gerade vermeiden, oder nicht? nein er wollte das runden geschickt gelöst bekommen. Bei einer Integerdivision wird ja nicht gerundet und darum wurde versucht es mit float zu lösen.
Peter II schrieb: > High Performer schrieb: >> as wird nicht funktionieren, da (4000000UL / va) eine Integerdivision >> darstellt. Das wolltest Du doch gerade vermeiden, oder nicht? > > nein er wollte das runden geschickt gelöst bekommen. Ja eben, und da bringt es nichts, erst eine Integer-Division zu machen und danach dann auf float zu erweitern und 0.5 abzuziehen. > Bei einer Integerdivision wird ja nicht gerundet und darum wurde versucht es mit > float zu lösen. Und genau das passiert beim obigen Beispiel nicht, und daher wird das nicht wie gewünscht funktionieren.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.