Forum: Mikrocontroller und Digitale Elektronik Fixed-Point Arithmetik oder doch wieder Float?


von Aaron K. (aaron_b_k)


Lesenswert?

Hi Leute,

ich versuche gerade eine Fixed-Point Bibliothek zu coden.

Mein Ansatz ist derzeit, dass ich die float-Werte ins Qm.n-Format 
formatiere.
Sprich ich habe dann m Vorkomma-Bits und n Nachkomma-Bits. Der Faktor, 
zum Umrechnen der Float Werte in fixe Werte, ist 2^n.

Der Exponent des Faktors und die mit dem Faktor verrechente Zahl werden 
jeweils in einem Integer gespeichert.

Der Faktor wird durch den Exponent immer so gewählt, dass die 
Vorkomma-Stellen gerade so dargestellt werden können.

Code ich gerade meinen eigenen Float-Datentype neben dem eigentlichen 
Datentyp oder ist das Beschriebene Vorgehen richtig ????

: Verschoben durch Moderator
von Andreas B. (bitverdreher)


Lesenswert?

Also ich würde das wieder als float definieren was Du da machst.
Fixed point ist eigentlich Integer wo Du Dir das Komma dazu denkst. 
Sprich, bei der Textausgabe das Komma einfügst.
Oder was schwebt Dir vor?

von Anarchist (Gast)


Lesenswert?

Auf welcher Zielplattform soll denn das laufen?

von Klaus W. (mfgkw)


Lesenswert?

Wenn du m änderst, ist es nicht mehr Festkomma.

Sind deine Zahlenbereiche so dynamisch, daß sich der Aufwand lohnt, oder 
ist es nicht sinnvoller wirklich bei Festkomma zu bleiben?
Dann wäre der der Rechenaufwand deutlich kleiner, auch wenn man deswegen 
vielleicht ein paar Bits mehr nehmen muß, die nicht immer genutzt 
werden. Fließkomma ist nicht umsonst zu haben.

von Aaron K. (aaron_b_k)


Lesenswert?

Das ganze soll dann auf eine dsPIC33 laufen. Ich würde gerne float 
umgehen, sofern das möglich ist. An sich reichen mir (für dieses 
Projekt) Werte von 9999,99 bis 0,00. Ich wollte mir nur Arbeit für die 
Zukunft sparen und eine möglichst universelle Bibliothek schreiben.

Iwann kam mir dann der Gedanke, dass das ja gar nicht mehr so 
verschieden von einem Float ist...

Ich denke es ist nicht verkehrt die Stelle des Kommas frei wählen zu 
können, jedoch sollte die Werte, die miteinander verrechnet werden, 
denselben Faktor besitzen, sodass diese schnell miteinander verrechnet 
werden können.

Ihr habt schon recht, wenn der Controller jedes mal den Exponenten 
anpassen muss, ist es im Grunde Gleitkomma darstellung und 
wahrscheinlich noch langsamer als mit Float zu rechnen.

Die Frage ist, ob es schneller ist, wenn das Komma fest steht und nicht 
weiter angepasst werden muss. Das muss ich mal austesten...

von Klaus W. (mfgkw)


Lesenswert?

Aaron K. schrieb:
> 9999,99

Du willst aber nicht mit dezimalen Stellen rechnen, hoffe ich?

Mit 14 Bits Vorkommastellen geht es bis über 16000. 13 Bits reichen 
nicht bis 9999.

Eine Genauigkeit von Hundertstel (oder besser) hätte man mit 7 Bits 
Nachkomma (1/128).

Macht zusammen 21 Bits mindestens.

Das ist jetzt wohl keine günstige Größe auf einem Rechner mit Bytes zu 8 
Bit, also würde man 24 Bits insgesamt nehmen.
Z.B. 17.7 oder 16.8... (binär, nicht dezimal).

Du musst natürlich den Wertebereich so einstellen, daß es auch für 
Zwischenergebnisse reicht.

von Klaus W. (mfgkw)


Lesenswert?

Aaron K. schrieb:
> eine möglichst universelle Bibliothek schreiben

es soll auch schon fertiges geben, übrigens :-)

von Andreas B. (bitverdreher)


Lesenswert?

Aaron K. schrieb:
> Iwann kam mir dann der Gedanke, dass das ja gar nicht mehr so
> verschieden von einem Float ist...

Allerdings ;-). Multipliziere die Zahl mit 100 und rechne mit 32bit 
Integer.
Bei der Ausgabe machst Du halt das Komma wieder rein.

Klaus W. schrieb:
> es soll auch schon fertiges geben, übrigens :-)

Ja, die 32-bit Integer Arithmetik. (Hab ich mal gehört ;-) ) 24-Bit 
dürfte dagegen schwieriger als fertige lib zu bekommen sein.

von c-hater (Gast)


Lesenswert?

Aaron K. schrieb:

> Ich wollte mir nur Arbeit für die
> Zukunft sparen und eine möglichst universelle Bibliothek schreiben.

Möglichst universell ist halt Gleitkomma. Deswegen ist Schwachsinn, 
Gleitkomma durch Gleitkomma einer eigenen Bibliothek ersetzen zu wollen.

Und für Festkomma braucht man keine Bibliothek. Für Festkomma braucht 
man nur Disziplin. Man muss sich halt an jeder Stelle des Codes im 
Klaren sein, wo die aktuelle Kommaposition ist. Idealerweise schreibt 
man den Code sogar so, dass die immer möglichst günstig für die nächste 
anzuwendende Operation liegt. Das Ziel ist dabei immer, möglichst nicht 
"breiter" werden zu müssen, aber trotzdem die angestrebte Genauigkeit 
bezüglich der Gesamtoperation einzuhalten.

Ja, man muss dazu den umzusetzenden Algorithmus betrachten. Und ja, man 
muss auch die Wertebereiche der Eingangsgrößen betrachten.

Allein daraus ergibt sich, dass es keine universelle Bibliothek für das 
Problem geben kann. Wäre sie wirklich unversell, wäre sie mit an 
Sicherheit grenzender Wahrscheinlichkeit auch gleich wieder suboptimal 
bezüglich des Durchsatzes (jedenfalls bei der überwiegenden Mehrheit der 
Probleme). So einfach ist das.

von Aaron K. (aaron_b_k)


Lesenswert?

c-hater schrieb:

> Allein daraus ergibt sich, dass es keine universelle Bibliothek für das
> Problem geben kann. Wäre sie wirklich unversell, wäre sie mit an
> Sicherheit grenzender Wahrscheinlichkeit auch gleich wieder suboptimal
> bezüglich des Durchsatzes (jedenfalls bei der überwiegenden Mehrheit der
> Probleme). So einfach ist das.

Da muss ich intervenieren... Man kann ja auch verschiedene 
Zahlenbereiche schaffen, in welchen sich das Komma immer an der selben 
Stelle befindet. Wäre dem nicht so, was wäre wenn in einem Bereich des 
Programms eine Auflösung bis in die 1000stel von nöten ist, in einem 
anderen jedoch eine einzige Nachkommastelle ausreichend genau ist?

In so einem Fall wäre es praktisch die Variablen welche im selben 
Zahlenbereich liegen miteinander verrechnen zu können. Gerade bei 
Division oder Multiplikation können die fixed-point-Zahlen nicht einfach 
miteinander verrechnet werden. Da:

Was man aber eig. möchte ist:

Also muss jedes Produkt durch den gemeinsamen Faktor geteilt werden um 
das erwünschte Ergebnis zu erhalten. Ebenso muss jeder Quotient mit dem 
gemeinsamen Skalierungsfaktor multipliziert werden, da:

Was man aber eig. möchte ist:

Diese Operationen jedesmal im Code "diskret" durchzuführen ist 
unübersichtlich und verschlechtert sowohl les- als auch wartbarkeit des 
Codes. Aus diesem Grund ist es Meiner Meinung nach besser sich eine 
kleine aber feine Bibliothek für die fixed-point-Arithmetik zu basteln.

Addition und Subtraktion sind wie gewohnt durchführbar, da gilt:

Der Übersichtlichkeit wegen bietet sich aber auch hier an Funktionen zu 
schreiben, sodass alle 4 Grundrechenoperationen im Code ähnlich 
aussehen.

Wenn man so möchte kann man diese kleine Bibliothek sicherlich auch so 
gestalten dass es verschiedene Skalierungsfaktoren gibt. Mann muss 
jedoch dafür sorgen dass Variablen, welche miteinander verrechnet 
werden, denselben Skalierungsfaktor besitzen. Den Skalierungsfaktor der 
Zahlen bei jeder Rechen-Operation anzupassen ist zwar gut für die 
Auflösung und die Code-Redundanz, jedoch begeht man dann denselben 
Fehler, aus welchem ich diesen Thread geöffnet habe und baut sich seinen 
eigenen Float-Datentype zusamenn. Die Berechnung des DIY-floats ist 
dabei höchst wahrscheinlich noch langsamer als den c-typischen 
float-Datentype zu verwenden.

Für meinen Code habe ich mich an dem hier inspiriert:

https://www.allaboutcircuits.com/technical-articles/fixed-point-representation-the-q-format-and-addition-examples/

Nach dieser Quelle berechnet sich die fixed-point-Zahl wie folgt:

Bit:
Anzahl der Bits:
Anzahl der Vorkomma-Bits:
Anzahl der Nachkomma-Bits/Skalierungs Faktor:

Um die fixed-point Zahl zu berechnen muss lediglich die float-Zahl mit 
dem Skalierungsfaktor multipliziert werden und am die restlichen 
Nachkommastellen "abgeschnitten" werden.

Dimensioniert man den Faktor auf 2^15, so ergibt sich eine Auflösung 
von:

und einen Wertebereich von:

Den Faktor kann man wie bereits erwähnt frei wählen, er sollte sich 
jedoch nachdem die Variable einmal in die fixed-point Darstellung 
konvertiert wurde, nur noch selten oder besser gar nicht mehr ändern.

Also nochmal zusammenfassend. Ja wenn an den Faktor bei jeder Operation 
anpasst, so hat man sich im Grunde einen eignenen float-Datentype 
gebastelt (weil ich deisen Fehler gemacht habe und mir nicht 100% sicher 
war habe ich diesen Thread geöffnet). Aber man kann sich sehr wohl eine 
Universelle Bibliothek basteln, welche für verschiedene Zahlenbereiche 
passend ist, basteln, so auch z.B. Für Zahlen zwischen 1 und -1, mit 
möglichst hoher Auflösung. Man sollte dabei jedoch aufpassen, dass man 
den Faktor eben gleich lässt und nicht anpasst, weil man dann besagtes 
Problem hat, dass man im Grunde erneut in float rechnet.

Bitte verbessert mich, wenn ich flasch liege, aber ich denke dass das 
Grundsätzlich mal nicht flasch ist was ich hier geschrieben habe. :D

Danke für alle bisher und alle die noch kommen eure Antworten ;-)

von M. Н. (Gast)


Lesenswert?

Klaus W. schrieb:
> Du musst natürlich den Wertebereich so einstellen, daß es auch für
> Zwischenergebnisse reicht.

Kleiner Hinweis: Jein. Wenn bspw. bei der Implementierung digitaler 
Filter sichergestellt ist, dass Eingangs- und Ausgangsgrößen im 
darstellbaren Bereich sind, können innendrin auch mal Sachen überlaufen, 
ohne dass etwas schiefgeht. Das ist mitunter einer der Vorteile von 
Fixed-Point Arithmetik.

Voraussetzung dafür ist, dass die Operationen alle linear sind und eben 
wie oben geschrieben, Eingangs- und Ausgangsgrößen garantiert immer im 
Wertebereich liegen

von LostInMusic (Gast)


Lesenswert?

>Code ich gerade meinen eigenen Float-Datentype neben dem eigentlichen
>Datentyp

Ja. Bei der Festkomma-Arithmetik führst Du ausschließlich 
Integer-Operationen aus. Das ist der Clou an der Sache. Fließkommazahlen 
haben einen variablen Exponenten, aber Festkommazahlen haben keinen 
festen Exponenten, sondern gar keinen. Verabschiede Dich von der Idee, 
Deine Festkomma-Zahlen könnten irgendwie als Exponent-behaftet 
interpretiert oder mit einem Exponenten aufgepeppt werden. Sobald Du 
daran auch nur denkst, bist Du im Float-Datentyp unterwegs.

Fällt Deine Entscheidung auf Fließkomma, benutze die eingebauten 
Single-/Double-Typen. Ist Dir Festkomma-Arithmetik lieber, rechne mit 
ausreichend großen Integer.

von Aaron K. (aaron_b_k)


Lesenswert?

Ich habe das ganze jetzt mal gemessen und nochmal ein wenig dazu 
recherchiert.

Also kurz um...
Addition und Subtraktion sind auf meiner Platform (dsPIC33CK) ca. um 
Faktor 10 schneller. Sie benötigen 25 statt wie beim Float 325 Schritte.

Multiplikation ist etwa gleich schnell. Fixed-Point benötigt hier 325 
und floating-point benötigt 147 Schritte.

Bei der Division wirds dann krass...
Die Division mit floats benötigt cs. 480 Schritte. Da ich in meinem 
fixed-point code 32-Bit verwende und ich diesen Bereich für die Division 
auf 64 erweitern muss, dauert die Division hier 9550 Zyklen...

Das Erweitern ist notwendig, da der erste fixed-point Wert mit dem 
Skalierungsfaktor multipliziert wird, sodass man auch Ergebniss kleiner 
1 oder kleiner -1 erhält. Mir fällt kein Weg das zu umgehen, vlt. wisst 
ihr ja weiter :)

Schlussendlich denke ich, dass ich wieder auf Float umsteige. Die 
Division dauert einfach deutlich zu lang nach der Erweiterung auf 64Bit. 
Bei der Multiplikation wird ebenfalls auf 64Bit erweitert um einem 
Überlauf vorzubeugen. Diese ist jedoch vergleichbar mit einer 
floating-point Operation, was die Zyklen angeht.

von Klaus (Gast)


Lesenswert?

c-hater schrieb:
> Allein daraus ergibt sich, dass es keine universelle Bibliothek für das
> Problem geben kann.

https://github.com/MikeLankamp/fpm

Also das Ding kommt einer universellen Bibliothek schön sehr nahe. Kann 
auch trigonometrische Funktionen.

Aber... UPS... C++,
Sorry C-Hater.

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.