Forum: Mikrocontroller und Digitale Elektronik ungenaue Festkommadarstellung


von Kevin (Gast)


Lesenswert?

Ich möchte Festkomma-arithmetik auf einen µC implementieren. Die 
Umwandlung float nach fix und vice versa erfolgt über folgendende 
Makros:

#define TOFIX(d, q) ((unsigned short)( (d)*(float)(1<<(q)) ))

#define TOFLT(a, q) ( (float)(a) / (float)(1<<(q)))

q gibt also die Nachkommastelle an. Eine Zahl soll als 16Bit gespeichert 
werden (unsigned short). Wenn ich z.b. für q = 4 einsetze bleiben also 
12Bit zur Darstellung der restlichen Zahl. Jedoch ist die Rechnung 
selbst bei einfacher Addition sehr ungenau.Beispielsweise die Addition 
folgender 2er Zahlen:

float d1 = 108.2f;
float d2 =  200.4f;

int q = 4;
unsinged short f1 = TOFIX(d1,q);
unsinged short f2 = TOFIX(d2,q);

unsigned short f3 = f1 + f2;

float flt1 = TOFLT(f3,q);
printf("converted float value:%f\n",flt1);

Konvertiert nach float ergibt 308.562500 statt den erwarteten 308.6. 
Selbst für q = 7 bekomme ich nicht den erwarteten Wert(wird natürlich 
genauer).
Ist der Fehler bei Festkommaarithmetik üblich? Und wenn ja, wie kann ich 
am besten die Fehlergenauigkeit bei gegebener Nachkommastelle berechnen, 
z.b. für q =1,2,3...
Wenn ich nun statt um q Bit zu schieben, den float Wert um 10 
multipliziere und das Ergebnis der Addition wieder dividiere, erhalte 
ich den erwarteten Wert:
1082+2004=3086 -> 3086/10= 308,6
Intuitiv hätte ich erwartet, das das schieben um 4 bzw 7Bit genauer ist 
als nur 1 Kommastelle zu verschieben.
Irgendwo habe ich hier einen Denkfehler ;-(

von Andreas S. (marais)


Lesenswert?

Überleg' Dir doch mal, welchen Wert jedes der 4 Bits der 
Nachkommastellen darstellt. Da gibt es Halbe, Viertel, Achtel und 
Sechzehntel. Das erklärt den Nachkommawert von 0.5625, das sind 9/16.

von g457 (Gast)


Lesenswert?

> Ist der Fehler bei Festkommaarithmetik üblich?

Nein. Aber Du machst keine Festkommaarithmetik sondern Rundungsfehler, 
und da ist das normal.

von Sebastian (Gast)


Lesenswert?

Kevin schrieb:
> 308.562500 statt den erwarteten 308.6.

Es ist genauer. Mit q=4 sind bei TOFLT Resultate im 1/16 Raster möglich, 
also 308.5, 308.5625, 308.625, ...

TOFIT rundet nicht, darum bekommst du 308.5625 als Ergebnis und nicht 
das nähere 308.625.

LG, Sebastian

von Kevin (Gast)


Lesenswert?

Wieso ergibt 4 Bits 9/16?

von (prx) A. K. (prx)


Lesenswert?

Zähle bitte alle Sechzehntel hier dezimal auf.

: Bearbeitet durch User
von weiter weg (Gast)


Lesenswert?

Kevin schrieb:
> Ich möchte Festkomma-arithmetik auf einen µC implementieren.

Wozu soll das Ganze gut sein?

Kevin schrieb:
> Irgendwo habe ich hier einen Denkfehler

Vielleicht erst mal das eigentliche Problem bzw. Ziel dar-
stellen bevor hier wieder gegen Windmühlen gekämpft wird.

von Axel S. (a-za-z0-9)


Lesenswert?

Kevin schrieb:
> Wieso ergibt 4 Bits 9/16?

<seufz>

4 Nachkomma-Bits ergeben eine Wertigkeit von 1/16 je Bit

von MaWin (Gast)


Lesenswert?

Axel S. schrieb:
> 4 Nachkomma-Bits ergeben eine Wertigkeit von 1/16 je Bit

Je LSB

von MagIO2 (Gast)


Lesenswert?

Ähm ... also ich würde mal sagen, dass das schon Festkomma-Arithmetik 
ist. Ist halt Festkomma-Arithmetik im Binär-System. Und ja, in der 
Binär-System-Festkomma-Arithmetik ist das ein übliches Problem.
Klar, es ist ein Rundungs-Problem, aber genauer gesagt würde ich es als 
Genauigkeits-Problem bezeichnen. Die 4 Nachkomma-Bits können nicht jede 
beliebige Dezimal-Zahl Nachkommastelle darstellen.

Alleine von 0,0 - 0,9 gibt es nur die 0,0 und die 0,5, die wirklich 
genau nach Binär-Festkomma umgewandelt werden können. Insgesamt gibt es 
bei 4 Bit naturgemäß nur 16 exakt darstellbare dezimale 
Nachkomma-Zahlen.

(Alle Kombinationen von 0,5  0,25  0,125  0,0625 und die 0)

Float hat im übrigen das gleiche Problem. Es tritt nur eben nicht so 
schnell auf, weil bei Float die Genauigkeit flexibler gehandhabt wird. 
Sprich, bei kleinen Zahlen kann Float genauer sein, als bei sehr hohen 
Zahlen. Und trotzdem gibt es auch Dezimalzahlen mit 2 Nachkomma-Stellen, 
wo Float bei wiederholten Rechnungen in Ungenauigkeiten läuft.

Eine andere Alternative wäre, die Festkomma-Arithmetik mit BCD-Zahlen 
(Binary Coded Decimal) zu machen. Dann gibt es bei der Genauigkeit 
keinen Unterschied mehr zur dezimalen Festkomma-Arithmetik. Allerdings 
ist dann die Umwandlung nach Float nicht mehr so einfach und auch die 
Operatoren funktionieren so nicht mehr (in standard C jedenfalls).

In C++ könnte man die entsprechend überladen.

Aber wie schon erwähnt, wenn man nicht genau weiß, was die Zielsetzung 
ist, stochert man im Trüben.

von Andreas S. (marais)


Lesenswert?

MagIO2 schrieb:
> Ähm ... also ich würde mal sagen, dass das schon Festkomma-Arithmetik
> ist. Ist halt Festkomma-Arithmetik im Binär-System.

Das ist doch Geschwurbel. Ist doch klar, dass wir hier vom Binärsystem 
reden. Was denn sonst? Von BCD war nie die Rede. Ich möchte behaupten, 
der TO kennt weder Festkomma noch BCD wirklich.

Beitrag #6725044 wurde von einem Moderator gelöscht.
von Jens (Gast)


Lesenswert?

Moin,

die Vorredner haben die Problematik ja gut erklärt, mehr ist unter 
diesen Umständen nicht rauszuholen.

Solltest Du allerdings meistens mit einer Nachkommastelle arbeiten, wäre 
statt des Shifts um 4 ein Teilen bzw. Multiplizieren mit 10 eine Lösung.

Oder mit einer anderen Zahl, die Deine Vorstellung von Genauigkeit 
besser trifft. 50 z.B. würde 0,02 als Auflösung liefern...

Gruß
Jens

von Wolfgang (Gast)


Lesenswert?

MagIO2 schrieb:
> Es tritt nur eben nicht so schnell auf, weil bei Float die Genauigkeit
> flexibler gehandhabt wird. Sprich, bei kleinen Zahlen kann Float genauer
> sein, als bei sehr hohen Zahlen.

Die relative Genauigkeit wird durch die Anzahl der Bits in der Mantisse 
bestimmt und ist auch bei Float für kleine und große Zahlen gleich.
Böse wird es bei der Addition/Subtraktion von stark unterschiedlichen 
Float Zahlen, weil die Operanden vor der Rechenoperation auf den 
gleichen Exponent gebracht werden müssen.

von MagIO2 (Gast)


Lesenswert?

Andreas S. schrieb:
> MagIO2 schrieb:
>> Ähm ... also ich würde mal sagen, dass das schon Festkomma-Arithmetik
>> ist. Ist halt Festkomma-Arithmetik im Binär-System.
>
> Das ist doch Geschwurbel. Ist doch klar, dass wir hier vom Binärsystem
> reden. Was denn sonst? Von BCD war nie die Rede. Ich möchte behaupten,
> der TO kennt weder Festkomma noch BCD wirklich.

Sorry .. hab vergessen das in den richtigen Bezug zu setzen:

g457 schrieb:
> Nein. Aber Du machst keine Festkommaarithmetik sondern Rundungsfehler,
> und da ist das normal.

Dachte, dass g457 möglicherweise die Festkomma-Arithmetik im 
Binär-System nicht erkannt hat.

;o)

von Pandur S. (jetztnicht)


Lesenswert?

Ich arbeite immer auch mit Festkomma Arithemtik. Der ADC hat vielleicht 
16-20Bit, und dann kommt oft ein Regler hinten dran. Der rechnet immer 
in 32bit Ganzzahl.
Irgendwo benoetige ich dann eine skalierung. zB ein Thermoelement an 
einem 20 Bit Wandler. Der Benutzer moechte Celsius eingebem, der Regler 
rechnet in hardware Werten, vom ADC her. Ein ADC Wert von vielleicht 
72623 waeren zB 279 Grad.

Ich moechte zB eine Aufloesung der Konstanten von vielleicht 1/1000 
haben, also 4 Stellen. Die division (62623/279) ergibt 260.297. So 
waehle ich meine Konstante als 1041, mit implizit div 4. Das ergibt dann 
bei 1040-> 279.31, resp 1041->279.05 Grad, Etwa 0.3 Grad bei 300Grad. 
Der Benutzer gibt seine 279 Grad als Vorgabewert ein und der 
Kalibriertwert 1040 als Multiplikator gefolgt von den Div 4 ergibt des 
Vorgabewert fuer den ADC.

von Stefan F. (Gast)


Lesenswert?

MagIO2 schrieb:
> Ähm ... also ich würde mal sagen, dass das schon Festkomma-Arithmetik
> ist. Ist halt Festkomma-Arithmetik im Binär-System. Und ja, in der
> Binär-System-Festkomma-Arithmetik ist das ein übliches Problem.

Das "Problem" existiert ebenso im Dezimalsystem. Wenn mein Thermometer 
21°C anzeigt muss ich ebenso davon ausgehen, dass das nicht die 
wirkliche Temperatur ist. Es könnte genau so gut 21,34°C sein, aber das 
Thermometer kann nur in 1° Schritten anzeigen.

von Schwaetzer (Gast)


Lesenswert?

> aber das Thermometer kann nur in 1° Schritten anzeigen.

Schon seit droellfzig Jahren schaffen es dickethale Thermometer
auch die Zehntel aufzuloesen. Ob die stimmen lassen wir mal
dahingestellt.

Stammt das vllt aus Wehrmachtsbestaenden?

von Stefan F. (Gast)


Lesenswert?

Schwaetzer schrieb:
> Schon seit droellfzig Jahren schaffen es dickethale Thermometer
> auch die Zehntel aufzuloesen. Ob die stimmen lassen wir mal
> dahingestellt.

Das ändert nichts am Sachverhalt.

Dann zeigt es halt 21,0°C an und ich muss damit rechnen, dass es in 
Wirklichkeit 21,0451353464213°C gemessen hat.

von Wolfgang (Gast)


Lesenswert?

Schwaetzer schrieb:
> Schon seit droellfzig Jahren schaffen es dickethale Thermometer
> auch die Zehntel aufzuloesen.

Nimm einen DS18B20. Der löst nicht Zehntel auf, sondern 1/16 Grad auf.
Wenn du die Werte als 1/10 Grad darstellst, sehen Temperaturverläufe 
trotz der höheren Auflösung sehr merkwürdig aus.

von Teo (Gast)


Lesenswert?

Kevin schrieb:
> Ich möchte Festkomma-arithmetik auf einen µC implementieren. Die
> Umwandlung float nach fix und vice versa....

Wo zu eigentlich dies hin und her Gehample?

von Andreas S. (marais)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das "Problem" existiert ebenso im Dezimalsystem.

Das "Problem" existiert in jedem Zahlensystem, egal ob die Basis jetzt 2 
oder 10 ... oder (warum nicht?) 5 ist.

von Stefan F. (Gast)


Lesenswert?

Andreas S. schrieb:
> Das "Problem" existiert in jedem Zahlensystem,

Ja, das ist der Punkt auf den ich hinaus wollte.

von Rolf M. (rmagnus)


Lesenswert?

Kevin schrieb:
> Wieso ergibt 4 Bits 9/16?

4 Bit nach dem Komma bedeuten, dass du deine Werte mit einer 
Schrittweite von 1/16 angeben kannst. Was dich vielleicht verwirrt: Eine 
Zahl, die in Dezimal sehr einfach aussieht, wie 0,6, muss im Dualsystem 
nicht genauso einfach sein. Tatsächlich ist 0,6 im Dualsystem eine 
periodische Zahl.

0,6 = 1/2 + 1/16 + 1/32 + 1/256 + 1/512 + 1/4096 + 1/8192 + …

Oder wenn man es direkt als duale Kommazahl schreiben würde:

0,1001100110011001100…

Wie du siehst, kannst du mit 4 Bit nur die ersten beiden der Summanden 
darstellen, der Rest geht verloren.

Schwaetzer schrieb:
> Schon seit droellfzig Jahren schaffen es dickethale Thermometer

Das ist genau die Art von Aufsatz, unter die der Lehrer dann "Thema 
verfehlt" schreibt.

von PittyJ (Gast)


Lesenswert?

Mir ist diese Festkomma Umrechnung viel zu kompliziert.
In meinen Programmen verschiebe ich einfach nur das Komma.
Temperaturen werden intern in 10tel-Grad gespeichert. Da reicht die 
Genauigkeit, die Umwandlung ist einfach un schnell, und selbst ein 
Mensch kann mit den Rohwerten etwas anfangen.
Spannungen speichere ich in in Millivolt. Genauer ist der AD-Wandler oft 
nicht, und bei uin16_t habe ich noch einen Bereich bis 65 Volt.

Und für Berechnungen brauche ich keine Macros, die über float gehen und 
auf einem Arm-M0 wegen fehlender FP-Einheit noch simuliert werden 
müssen.

von Wolfgang (Gast)


Lesenswert?

PittyJ schrieb:
> In meinen Programmen verschiebe ich einfach nur das Komma.
> Temperaturen werden intern in 10tel-Grad gespeichert.

Viel Spaß, wenn die Werte aus einem DS18B20 stammen. Jede vernünftige 
Auswertung von Temperaturänderungen wird damit dann zur Farce, wenn man 
sich nicht gerade auf "so um ± 0.2" beschränkt.

von Teo (Gast)


Lesenswert?

Wolfgang schrieb:
> Viel Spaß, wenn

Er schrieb "gespeichert" nicht angezeigt.
Evtl. will man damit ja auch noch a wengerl rum rechnen.

von Wolfgang (Gast)


Lesenswert?

Teo schrieb:
> Er schrieb "gespeichert" nicht angezeigt.

Eben - und wenn jemand mit den gespeichert Werten irgendetwas tut, z.B. 
einen Verlauf graphisch darstellen, sieht er den Salat.
Dann meint er, plötzlich ungleichmäßige Temperaturänderungen zu haben, 
obwohl der Verlauf eigentlich völlig gleichmäßig war.

von Kevin (Gast)


Lesenswert?

PittyJ schrieb:
> Mir ist diese Festkomma Umrechnung viel zu kompliziert.
> In meinen Programmen verschiebe ich einfach nur das Komma.


Das hatte ich auch im Anfangsthread erwähnt. Einfach um 10,100,1000... 
multiplizieren. Wenn man den un-skalierten Wert haben möchte 
entsprechend dividieren.Viel einfacher als Festkomma und genauer.Darum 
wundere ich mich schon, das Festkomma-Rechnungen so verbreitet sind.

Man könnte nun für bessere Genauigkeit einen 32Bit Wert reservieren d.h. 
16Bit für Ganzzahl und 16Bit für die Nachkommastelle. Aber ich möchte 
auch Werte miteinander multiplizieren. Und 64Bit stehen nicht zur 
Verfügung als Wortlänge. Leider weiß ich nicht wie ich Überlaufe dann 
handeln soll.So die Idee mich auf 2Byte zu beschränken,mit 4Bit als 
Nachkomma und 12Bit für die Ganzzahl.

von Falk B. (falk)


Lesenswert?

Kevin schrieb:
> Das hatte ich auch im Anfangsthread erwähnt. Einfach um 10,100,1000...
> multiplizieren. Wenn man den un-skalierten Wert haben möchte
> entsprechend dividieren.Viel einfacher als Festkomma und genauer.

Das IST Festkommaarithmetik! Niemand sagt, daß man mit 1/2^n 
Auflösung arbeiten MUSS!!

von Kevin (Gast)


Lesenswert?

Falk B. schrieb:
> Kevin schrieb:
>> Das hatte ich auch im Anfangsthread erwähnt. Einfach um 10,100,1000...
>> multiplizieren. Wenn man den un-skalierten Wert haben möchte
>> entsprechend dividieren.Viel einfacher als Festkomma und genauer.
>
> Das IST Festkommaarithmetik! Niemand sagt, daß man mit 1/2^n
> Auflösung arbeiten MUSS!!

Es ist aber weit verbreitet, vermutlich um shiften zu können.

von Blechbieger (Gast)


Lesenswert?

Kevin schrieb:
> Einfach um 10,100,1000...
> multiplizieren. Wenn man den un-skalierten Wert haben möchte
> entsprechend dividieren.Viel einfacher als Festkomma und genauer.Darum
> wundere ich mich schon, das Festkomma-Rechnungen so verbreitet sind.

Weil früher generell und heute auch noch je nach Mikrocontroller 
Multiplikation und insbesondere Division sehr langsame Operationen 
waren. Shiften war sehr viel schneller aber halt nur mit Zweierpotenzen 
möglich. Heutzutage sind Mikrocontroller allgemein viel schneller und 
Multiplikation meistens genau so schnell wie Shiften.

von c-hater (Gast)


Lesenswert?

Kevin schrieb:

> Das hatte ich auch im Anfangsthread erwähnt. Einfach um 10,100,1000...
> multiplizieren. Wenn man den un-skalierten Wert haben möchte
> entsprechend dividieren.Viel einfacher als Festkomma und genauer.

"Genauer" ist es dann (und NUR dann), wenn es einzig darum geht, 
diesen einen Wert später zu einer dezimalen Anzeige zu verwursten.

> Darum
> wundere ich mich schon, das Festkomma-Rechnungen so verbreitet sind.

Weil es immer DANN genauer wird, wenn man mit dem Kram irgendwas 
rechnen muss. Gerechnet wird nämlich im Binärsystem (Ausnahme: 
BCD-Arithmetik).

Und gerechnet wird in vielen Anwendungen. Regler, digitale Filter usw. 
usf.

> Man könnte nun für bessere Genauigkeit einen 32Bit Wert reservieren

Natürlich, umso mehr Bits, umso genau wird's. Aber auch umso langsamer. 
Spätestens, wenn man den Datenbereich der nativen Typen des Zielsystems 
verlassen muss, um eine hinreichende Genauigkeit für die konkrete 
Anwendung zu erzielen.
Und blöderweise ist es ausgerechnet dort, wo gerechnet werden muss oft 
auch so, dass die Rechnerei ziemlich flott sein muss, damit die 
Anwendung noch funktionieren kann. Bei einem Regler nützt es garnix, 
wenn du den nächsten Stellwert auf dreißig Stellen nach dem Komma 
ausrechnest, wenn der Aktor überhaupt nur mit 12 Bit angesteuert werden 
kann. Was aber u.U. viel nützt, ist wenn das Ergebnis dieser Berechnung 
20000mal pro Sekunde zu verfügung steht und nicht nur alle zwei 
Sekunden.

Genau sowas ist der Anwendungsbereich von Ganzzahl-Arithmetik.

> 16Bit für Ganzzahl und 16Bit für die Nachkommastelle. Aber ich möchte
> auch Werte miteinander multiplizieren. Und 64Bit stehen nicht zur
> Verfügung als Wortlänge.

Dann muss man halt diese Möglichkeit schaffen. Die Grundrechenarten sind 
easy.

> Leider weiß ich nicht wie ich Überlaufe dann
> handeln soll

Vorzugsweise richtig.

von Wolfgang (Gast)


Lesenswert?

Blechbieger schrieb:
> Weil früher generell und heute auch noch je nach Mikrocontroller
> Multiplikation und insbesondere Division sehr langsame Operationen
> waren.

Früher (tm) hat man oft einfach in der Anzeige die LED für den 
Dezimalpunkt an der passende Stelle aktiviert. Das kostete den µC einen 
einzigen IO-Zugriff, ganz ohne Multiplikation, Float-Rechnerei oder 
Binär-Dezimal-Diskussion.

von Rolf M. (rmagnus)


Lesenswert?

Kevin schrieb:
> PittyJ schrieb:
>> Mir ist diese Festkomma Umrechnung viel zu kompliziert.
>> In meinen Programmen verschiebe ich einfach nur das Komma.
>
> Das hatte ich auch im Anfangsthread erwähnt. Einfach um 10,100,1000...
> multiplizieren. Wenn man den un-skalierten Wert haben möchte
> entsprechend dividieren.Viel einfacher als Festkomma und genauer.

Es ist nicht einfacher. Ob ich durch 10 oder durch 16 teile, macht 
theoretisch keinen Unterschied. In der Praxis ist das Teilen durch 16 
viel einfacher, weil durch einen simplen Bit-Shift lösbar.
Und genauer (besser gesagt höher auflösend) ist es auch nicht, wenn du 
Werte in einer Schrittweite von 1/10 statt 1/16 speicherst. Im 
Gegenteil. Und es sind eben einfach andere Werte, die damit exakt 
darstellbar sind. Bei 1/10 wären deine 0,6 exakt darstellbar, da das ein 
ganzzahliges Vielfaches von 1/10 ist. Dafür ist bei 1/16 eben z.B. 
0,4375 exakt darstellbar, da das ein ganzzahliges Vielfaches von 1/16 
ist. Das müsstest du bei 1/10-Schritten auf 0,4 runden. Aber 16 Schritte 
sind mehr als 10, also ist die Auflösung höher.

> So die Idee mich auf 2Byte zu beschränken,mit 4Bit als Nachkomma und
> 12Bit für die Ganzzahl.

Damit kannst du dann Werte bis maximal 4095,9375 darstellen mit einer 
Auflösung von 1/16. Würdest du durch 10 teilen, hättest du eben einen 
Maximalwert von 6553,5, dafür nur noch eine Auflösung von 1/10.

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.