Forum: Compiler & IDEs float Berechnung mit int-Werten erzwingen


von Peter (Gast)


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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von Peter (Gast)


Lesenswert?

Ok, vielen Dank! Da mir "float"-Genauigkeit genügt, werde ich somit die 
3. Variante benutzen.

von Peter II (Gast)


Lesenswert?

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.

von Peter (Gast)


Lesenswert?

Ja, wegen dem runden. Das war nur ein Zahlenbeispiel, va ist in der 
Applikation variabel!

von Peter (Gast)


Lesenswert?

..und die floatrechnung "kostet" nichts, ich habe da einen MPC8314 mit 
einem PowerPC e300c3 Core, der hat eine FPU intus!

von Peter II (Gast)


Lesenswert?

Peter schrieb:
> Ja, wegen dem runden.

dann rechne doch anders:

vb = (4000000+(va/2)) / va;

(so in der art - hoffentlich ist kein fehler drin)

von Karl H. (kbuchegg)


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.

von Peter (Gast)


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?

von Peter II (Gast)


Lesenswert?

Peter schrieb:
> Ob das wirklich schneller ist?

ich denk schon, immerhin fehlt die ganze float int wandlung weg.

von Karl H. (kbuchegg)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von High Performer (Gast)


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".

von Peter II (Gast)


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.

von Peter II (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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."

von Peter II (Gast)


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.

von (prx) A. K. (prx)


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.

von High Performer (Gast)


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.

von Peter II (Gast)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

genauer: 2 Fehler, denn es heißt wegen des Rundens :-)

von Der Weise (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


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.

von Rolf Magnus (Gast)


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.

von Peter II (Gast)


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.

von (prx) A. K. (prx)


Lesenswert?

Jep, wenn man genug Takte zwischen die FPU-Divisionssequenz und die 
davon abhängigen Integer-Operationen einschieben kann, dann kann sich 
das lohnen.

von Peter (Gast)


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;

von Peter II (Gast)


Lesenswert?

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

von Peter (Gast)


Lesenswert?

Noch was: Ich denke ich muss bei einem 32 Bitter die Konstante nicht mit 
einem Long-Attribute versehen?

vb = int((4000000UL / va) - 0.5)

von (prx) A. K. (prx)


Lesenswert?

Das musst du auch sonst nicht. Eine dezimale lexikalische Konstante 
kriegt automtisch den kleinsten Integer Typ mit Vorzeichen in den sie 
reinpasst, mindestens int.

von High Performer (Gast)


Lesenswert?

>vb = int((4000000UL / va) - 0.5)

Das wird nicht funktionieren, da (4000000UL / va) eine Integerdivision 
darstellt. Das wolltest Du doch gerade vermeiden, oder nicht?

von Peter II (Gast)


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.

von (prx) A. K. (prx)


Lesenswert?

Wie wärs mit
 vb = ((8000000 / va) + 1) / 2;

von Rolf Magnus (Gast)


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.

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
Noch kein Account? Hier anmelden.