float Berechnung mit int-Werten erzwingen

Gast #2492763
Lesenswert?

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?
#2492911
Lesenswert?

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.
Gast #2492949
Lesenswert?

>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?
#2492968
Lesenswert?

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.
Moderator Persönliche Seite #2492973
Lesenswert?

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.
Gast #2492987
Lesenswert?

>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".
Gast #2492988
Lesenswert?

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.
Moderator Persönliche Seite #2493005
Lesenswert?

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."
Gast #2493015
Lesenswert?

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.
#2493027
Lesenswert?

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.
Gast #2493036
Lesenswert?

>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.
#2493062
Lesenswert?

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.
Gast #2493309
Lesenswert?

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.
Gast #2493314
Lesenswert?

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.
Gast #2493349
Lesenswert?

>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;
Gast #2493378
Lesenswert?

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.
Gast #2493402
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren