Forum: Mikrocontroller und Digitale Elektronik Festkommazahl Konsolenausgabe


von Franky (Gast)


Lesenswert?

Hallo Liebe Leute,

Folgendes:
Ich habe arbeitsbedingt zur Zeit mit einem 32Bit µC zu tun, welcher 
KEINE FPU hat, weswegen an einigen Stellen aufgrund der höheren 
Effizienz Festkommaarithmetik auftritt. Soweit so gut, damit habe ich 
vom Verständnis her nicht weiter Probleme.

Nun steh ich grad vor folgendem Problem, dessen Lösung sich mir seit 2 
Tagen nicht erschließt und ich deshalb jetzt mal eine außenstehende Idee 
brauche um ggf. etwas klarer zu sehen:

Die Festkommazahlen werden als Q0.31 Zahlen behandelt (1Vorzeichen+31Bit 
Nachkommastelle). Einige simple Funktionen wie die Multiplikation sind 
implementiert und liefern richtige Ergebnisse. So und nun das Problem: 
Für ein paar Debug ausgaben, möchte ich mir gerne einige der Ergebnise 
auf der Konsole anzeigen lassen. An sich kein Problem, aber 
Festkommazahlen sind ja für den µC nichts weiter als signed int's, 
welche er auch so ausgibt. Vor allem bei 32Bit werden damit die Zahlen 
extrem unansehnlich; Beispiel:

+0,5 gelesen als Q0.31 ist in Binärdarstellung:

0|,1000000000....0

das entspricht 2^30 = 1073741824 als int gelesen .... das ist sehr wenig 
anschaulich.

Nun wollte ich mir gerne wirklich mittels einer Konsolenausgabe 
(meinetwegen printf() ) anzeigen lassen, dass das 0,5 ist und nicht 
sonstewas ... und das bekomme ich einfach nicht hin. Vor allem bei 
heftigeren Zahlen, meinetwegen 0,81231236123 oder so, muss ich ja 
sozusagen die Nachkommabits abhängig von ihrer Wertigkeit addieren ... 
aber das werden dann ja wieder float Zahlen :D

Vielleicht sehe ich einfach grade den Wald vor lauter Bäumen nicht mehr 
und es ist ganz einfach. Achso, programmiert wird in C.


Grüße und frohe Ostern

von M. N. (Gast)


Lesenswert?

Franky schrieb:
> Ich habe arbeitsbedingt zur Zeit mit einem 32Bit µC zu tun

Und dafür gibt es keine math.lib mit 'float' und 'double'?
Das glaube ich nicht!

von Franky (Gast)


Lesenswert?

Natürlich gibt es die!
Das ist aber nicht Sinn der Übung, ich habe eine Portierung vorzunehmen, 
der Code arbeitet bereits mit Festkommaarithmetik und diese ist aus 
Recheneffizienzgründen nicht mit floats zu behandeln, da dass ohne FPU 
extrem auf die Rechenleistung drückt ...

Immer diese Besserwisser-Antworten hier ...

von Falk B. (falk)


Lesenswert?

@  Franky (Gast)

>Die Festkommazahlen werden als Q0.31 Zahlen behandelt (1Vorzeichen+31Bit
>Nachkommastelle).

Nö, das ist nicht zwingend. Siehe Festkommaarithmetik.

>Nun wollte ich mir gerne wirklich mittels einer Konsolenausgabe
>(meinetwegen printf() ) anzeigen lassen, dass das 0,5 ist und nicht
>sonstewas ... und das bekomme ich einfach nicht hin.

Wirklich? Und du machst was mit 32 Bit Prozessoren?

> Vor allem bei
>heftigeren Zahlen, meinetwegen 0,81231236123 oder so, muss ich ja
>sozusagen die Nachkommabits abhängig von ihrer Wertigkeit addieren ...

Dann tu das.

>aber das werden dann ja wieder float Zahlen :D

Nö. Man rechnet mit Integerdivision und Modulo. Siehe Artikel oben.

>Vielleicht sehe ich einfach grade den Wald vor lauter Bäumen nicht mehr
>und es ist ganz einfach. Achso, programmiert wird in C.

uint32  x=123456;

printf("%d.%d",x/100, x % 100);

> 1234.56

Du besitzt ein C-Grundlagenbuch? Wenn nein, solltest du dir schnellstens 
eins kaufen.

von Viktor N. (Gast)


Lesenswert?

Fuer die Anzeige darf man float verwenden. Denn die Anzeige ist nicht so 
haeufig. Wenn man den Display alls 100ms neu beschreibt wird es zum 
Ablesen schon grenzwertig schnell. Eher nur alle 300ms.

von Falk B. (falk)


Lesenswert?

@  Franky (Gast)

>Recheneffizienzgründen nicht mit floats zu behandeln, da dass ohne FPU
>extrem auf die Rechenleistung drückt ...

Sicher, aber für die Ausgabe auf der Konsole ist das auch ohne FPU 
schnel genug.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Franky schrieb:
> (1Vorzeichen+31Bit Nachkommastelle)
Ist das wirklich so?
Gibt es in diesem Format also eine +0 und eine -0?
+0 = 00000000000000000000000000000000
-0 = 10000000000000000000000000000000

Oder ist es eher wie bei einer Integerzahl, wo führende Nullen eine 
positive und führende Einsen eine negative Zahl anzeigen?

Wie genau soll diese Anzeige sein?
Reichen da evtl. schon die führenden 5 Stellen nach dem Komma?

von Franky (Gast)


Lesenswert?

Falk Brunner schrieb:
> @  Franky (Gast)
>
>>Die Festkommazahlen werden als Q0.31 Zahlen behandelt (1Vorzeichen+31Bit
>>Nachkommastelle).
>
> Nö, das ist nicht zwingend. Siehe Festkommaarithmetik.
>
>>Nun wollte ich mir gerne wirklich mittels einer Konsolenausgabe
>>(meinetwegen printf() ) anzeigen lassen, dass das 0,5 ist und nicht
>>sonstewas ... und das bekomme ich einfach nicht hin.
>
> Wirklich? Und du machst was mit 32 Bit Prozessoren?
>
>> Vor allem bei
>>heftigeren Zahlen, meinetwegen 0,81231236123 oder so, muss ich ja
>>sozusagen die Nachkommabits abhängig von ihrer Wertigkeit addieren ...
>
> Dann tu das.
>
>>aber das werden dann ja wieder float Zahlen :D
>
> Nö. Man rechnet mit Integerdivision und Modulo. Siehe Artikel oben.
>
>>Vielleicht sehe ich einfach grade den Wald vor lauter Bäumen nicht mehr
>>und es ist ganz einfach. Achso, programmiert wird in C.
>
>
> Du besitzt ein C-Grundlagenbuch? Wenn nein, solltest du dir schnellstens
> eins kaufen.

Hallo Falk,

sowohl mit von dir genanntem Link als auch mit einem anderen Thema hier 
im Forum habe ich mich bereits befasst, wenn mir das geholfen hätte, 
hätte ich nichts gepostet.

Also erstmal: Q0.31 ist für das Programm vereinbart definiert wie die 
Zahlen zu interpretieren sind, das gilt IMMER für dieses Programm, wenn 
es nunmal so definiert ist...reine Vereinbarungssache 
programmspezifisch. Verstehe deinen Kommentar dazu nicht, dass es auch 
andere Vereinbarungen wie meinetwegen Q1.14 etc. gibt, war nicht meine 
Frage.


> uint32  x=123456;
>
> printf("%d.%d",x/100, x % 100);
>
>> 1234.56

Ja, das habe ich bereits gelesen in erwähntem anderen Thread. Dann mach 
das mal mit 2^30 und beweise mir, dass dir die Konsole 0,5 anzeigt, was 
sie nicht tun wird.

Naja, ich merks nach den paar Posts schon, dass man sich eh wieder nur 
was anhören muss, wie dumm man ist... Danke für die Diskussion und 
trotzdem frohe Ostern

von Franky (Gast)


Lesenswert?

Lothar Miller schrieb:
> Franky schrieb:
>> (1Vorzeichen+31Bit Nachkommastelle)
> Ist das wirklich so?

Ja, das ist so

> Gibt es in diesem Format also eine +0 und eine -0?
> +0 = 00000000000000000000000000000000
> -0 = 10000000000000000000000000000000

öhhh, nein

> Oder ist es eher wie bei einer Integerzahl, wo führende Nullen eine
> positive und führende Einsen eine negative Zahl anzeigen?

So schon eher :)

> Wie genau soll diese Anzeige sein?
> Reichen da evtl. schon die führenden 5 Stellen nach dem Komma?

Ja, das ist erstmal nicht so wichtig.... zum funktionieren reichen mir 
auch ein paar Stellen.

von Karl H. (kbuchegg)


Lesenswert?

Franky schrieb:

> Naja, ich merks nach den paar Posts schon, dass man sich eh wieder nur
> was anhören muss, wie dumm man ist... Danke für die Diskussion und
> trotzdem frohe Ostern

Nö.
Aber wie schon gesagt, ist es zum Debuggen ziemlich unerheblich, ob 
Floating Point mal ein wenig länger dauert.

  (double)Zahl / (( 1U<<32)-1)

das ganze als double gerechnet und ab auf die Anzeige damit.

von Karl H. (kbuchegg)


Lesenswert?

Franky schrieb:

> Ja, das ist erstmal nicht so wichtig.... zum funktionieren reichen mir
> auch ein paar Stellen.

Und?
Wo liegt dann jetzt das Problem?
Du weißt wie sich die Bits addieren. Bau dir halt eine Schleife auf, die 
genau das tut, was du sonst händisch machst. Wenn du mit 5 Stellen 
zufrieden bist, dann benutzt du halt dort auch wieder 
Fixkomma-Arithmetik und stellst 0.5 mittels 50000 dar. Das Nächste Bit 
sind dann 0.25, also 25000, das nächste 0.125, also 12500 usw. usw.
Alles als Ganzzahl zusammenaddiert, noch ein 0_Komma davor und die Zahl 
mit führenden 0-en 5-stellig dahinter ausgegeben und schon wird dir
0b01000000000000000000000000000000
als
  0.50000
ausgegeben. Das dieser 'Text' sich aus 2 Teilen zusammensetzt und da 
eigentlich '0.' gefolgt von 50000 steht, ist dir ja wieder wurscht. Das 
was du liest, entspricht der ursprünglichen Binärzahl.

Vorzeichenbehandlung nicht vergessen!

von M. N. (Gast)


Lesenswert?

Franky schrieb:
> Das ist aber nicht Sinn der Übung, ich habe eine Portierung vorzunehmen,
> der Code arbeitet bereits mit Festkommaarithmetik und diese ist aus
> Recheneffizienzgründen nicht mit floats zu behandeln, da dass ohne FPU
> extrem auf die Rechenleistung drückt ...
>
> Immer diese Besserwisser-Antworten hier ...

Vermutlich weißt Du garnicht, welche CPU Du überhaupt verwendest. 
Verraten wird's jedenfalls nicht.
Und Du weißt auch nicht, wie schnell diese mit 'float' rechnen können - 
auch ohne FPU.
Dass Festkommaberechnungen signifikant schneller sein sollen als 'float' 
- wenn überhaupt, müßtest Du mir nachweisen. Auf dem Auge der bloßen 
Vermutungen bin ich nämlich blind.

von Falk B. (falk)


Lesenswert?

@  Franky (Gast)

>Also erstmal: Q0.31 ist für das Programm vereinbart definiert wie die
>Zahlen zu interpretieren sind, das gilt IMMER für dieses Programm, wenn
>es nunmal so definiert ist...reine Vereinbarungssache

Sicher.

> uint32  x=123456;
>
> printf("%d.%d",x/100, x % 100);
>
>> 1234.56

>Ja, das habe ich bereits gelesen in erwähntem anderen Thread. Dann mach
>das mal mit 2^30 und beweise mir, dass dir die Konsole 0,5 anzeigt, was
>sie nicht tun wird.

1.) kann man für Debugausgaben float/double benutzen, ist schnell genug.

2.) kann man das ganz einfach mit dem bekannten Schema aus der Schule 
berechnen und ausgeben. Der Haken liegt halt bei der Kodierung zur Basis 
2, das macht die Sache ein wenig nerviger.

"das entspricht 2^30 = 1073741824 als int gelesen .... das ist sehr 
wenig
anschaulich."

Klar.

2^30 / 2^31 = 1073741824 / 2147483648 = 0 Rest 1073741824

Hier setzt die normale Division ein, wie man sie mal gelernt hat.

1073741824 * 10 / 2147483648 = 5 Rest 0
Rest * 10 / 2147483648 ...

-> 0,5

Klar, 2^30 * 10 > 32 Bit. Dafür gibt es zwei Lösungsmöglichkeiten.

1.) 64 Bit int
2.) die letzte Stelle in den Skat drücken und vor den Rechungen /2 
teilen und immer / 1073741824 rechnen.

>Naja, ich merks nach den paar Posts schon, dass man sich eh wieder nur
>was anhören muss, wie dumm man ist...

Nö, aber Kritik muss man schon einstecken können. Den C-Code dazu 
solltest du selber hinkriegen. Ob der dann effizienter als die 
Verwendung von double ist, bleibt offen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> stellst 0.5 mittels 50000 dar
Dazu ist es natürlich nötig, das Zahlensystem (wenigstens mental) zu 
wechseln und auf einen richtigen Integer zu wechseln, und den dann auch 
darzustellen.

Und wenn es eine negative Zahl ist, dann merk dir das, invertiere die 
Zahl und mach so weiter, wo Karl Heinz angefangen hat. Das wars dann...

Alternativ könntest du deine 32-Bit-Zahl auch nur als Signed Integer 
ansehen und z.B. durch 21475 teilen. Lass dich überraschen, was dabei 
rauskommt...

von Falk B. (falk)


Lesenswert?

265121435   / 2147483648 = 0 Rest 265121435
2651214350  / 2147483648 = 1 Rest 503730702
5037307020  / 2147483648 = 2 Rest 742339724
7423397240  / 2147483648 = 3 Rest 980946296
9809462960  / 2147483648 = 4 Rest 1219528368
12195283680 / 2147483648 = 5 Rest 1457865440
14578654400 / 2147483648 = 6 Rest ??? Overflow im Windows Taschenrechner

von Falk B. (falk)


Lesenswert?

Naja, man könnte auch die krampfhafte Normierung von

1 = 2^31 = fallen 1.073.741.824 lassen und gescheit auf 1 = 
1.000.000.000 normieren. Dann rechnet es sich 1. viel einfacher und 
schneller und man hat keine Problem mit nicht exakt darstellbaren Zahlen 
im 2er und 10er System . . .
Wäre aber zu pragmatisch

von Fabian O. (xfr)


Lesenswert?

Oder man muss halt für die Ausgabe mit einem 64-Bit-Zwischenergebnis 
rechnen:
1
int32_t ausgabe = (int64_t) zahl * 1000000000 / (1L << 31);

von PittyJ (Gast)


Lesenswert?

Warum nicht also Float.

Ganz früher hatte ich mal einen Z80-Rechner (2MHz). Der hat in Basic 
immer mit floats gerechnet. Und selbst damit konnte man etwas 
vernünftiges anfangen.

Letztens hatte ich einen TI2810 Mikrocontroller. Der hat Floats auch 
emuliert, und lief nur mit 100 MHz. Doch auch damit habe ich ADC-Werte 
sklaliert in normaler IEEE-754 Darstellung. Und der hatte noch Zeit für 
1000 andere Dinge.
So eine Float-Emulation kann genz schon schnell sein. Und bevor ich als 
Programmierer mit Festkommarechnung überfordert bin, probiere ich erst 
mal die Floats aus. Optimieren kann man später.

von Stefan W. (dl6dx)


Lesenswert?


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.