Forum: Mikrocontroller und Digitale Elektronik Datentypkonvertierung uint16_t zu double


von Fabian (Gast)


Lesenswert?

Moin,
bisher habe ich es immer vermieden mit float und double zu arbeiten, 
weil es mir unheimlich ist. Da ich nun aber auch sin + cos verwenden 
muss, komme ich nicht drumherum.

Nun habe ich auch schon das erste Problem. Ich habe drei Werte in 
uint16_t und ich benötige jeweils drei double Werte im Bereich von 0..1, 
die das Verhältnis wiederspiegeln. Also habe ich es wie folgt versucht 
zu lösen. (Vereinfachtes Beispiel)

Prozessor: ARM Cortex M3 (STM32F103)
Compiler: ARM Yagarto
1
uint16_t a = 1054;
2
uint16_t b = 84;
3
uint16_t c = 1403;
4
5
double all = a + b + c;
6
double a_ = (double)a / all;
7
double b_ = (double)b / all;
8
double c_ = (double)c / all;

Hier ist mir aufgefallen, dass der b Anteil viel zu groß erscheint. Also 
habe ich mir mal die konvertierten Zahlen ausgegeben:
1
uint16_t a = 1054;  // Nach (double)a steht hier 2048 drin
2
uint16_t b = 84;    // Nach (double)b steht hier 855 drin
3
uint16_t c = 1403;  // Nach (double)c steht hier 2048 drin

Kann das sein?! - So schlecht kann man doch gar nicht auf / abrunden bei 
der Typkonvertierung.

Was mache ich falsch?

Vielen Dank für eure Unterstützung.

von Jobst M. (jobstens-de)


Lesenswert?

Fabian schrieb:
> // Nach (double)a steht hier 2048 drin
> // Nach (double)b steht hier 855 drin
> // Nach (double)c steht hier 2048 drin

1. Was steht denn vorher drin?
2. Wie prüfst Du das?


Gruß
Jobst

von Jim M. (turboj)


Lesenswert?

Fabian schrieb:
> uint16_t a = 1054;  // Nach (double)a steht hier 2048 drin

Eventuell kommt auch nur der Debugger beim Anzeigen durcheinander. Der 
Compiler arbeitet ja intern mit den (32-bittigen) Registern, und für 
double müsste man gleich 2 zusamman auswerten.

Hier würde ich mir den generierten Assembler Code zusammen mit den 
Regsiter Inhalten genauer anschauen.

Vorsicht: Als ich das letzte Mal yagarto benutzt habe, war (*)printf für 
float Formate (%f etc.) kaputt. Zur Anzeige habe ich die Werte in 
Ganzzahlen zurück gewandelt. Die Anzeige der Variablen im Debugger 
sollte das aber nicht betreffen.

von Fabian (Gast)


Lesenswert?

Vorher stehen die richtigen Werte drin. Ich prüfe es so:
1
sprintf("Vorher[%u] Nachher[%f]", a, (double)a);

Ergibt dann:
Vorher[1138] Nachher[2048]

von Wolfgang (Gast)


Lesenswert?

Fabian schrieb:
> uint16_t und ich benötige jeweils drei double Werte im Bereich von 0..1,
> die das Verhältnis wiederspiegeln.

Was für einen Compiler benutzt du?

Der Datentyp double ist bei vielen µC-Compilern nur Augenwischerei und 
wird tatsächlich als float umgesetzt, i.e. mit 7-8 signifikanten Stellen 
und nicht mit 15-16, wie man es vielleicht erwarten könnte.

von Fabian (Gast)


Lesenswert?

Oh mann, da war ich völlig auf der falschen Spur. Das Problem lag ganz 
wo anders. Hinter a, b und c befinden sich in Wirklichkeit Pointer auf 
Werte und dieser Speicherbereich war nicht mehr gültig, da er im Stack 
lag. Nun funktioniert alles wie erwartet!

Vielen Dank euch, ich gehe jetzt mit gesenktem Kopf...

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Fabian schrieb:
> Vielen Dank euch, ich gehe jetzt mit gesenktem Kopf...

Passiert :)

von Adib (Gast)


Lesenswert?

.... Aber Vorsicht bei deinem printf Format!

%u erwartet unsigned int, du hast uint_16
%f erwartet float, du hast double.

Möglicherweise funktioniert das implementierungsbedingt bei dir.
Du solltest die Variablen entsprechend den Vorgaben der Formatbezeichner 
casten.

Grüße, Adib.

von Adib (Gast)


Lesenswert?

Uhh, hab noch mal nachgeschaut. Printf kennt kein float.
Also %f ist double.
gesenkten Kopfes, ... ,
Adib.

von Peter D. (peda)


Lesenswert?

Adib schrieb:
> Uhh, hab noch mal nachgeschaut. Printf kennt kein float.
> Also %f ist double.

So ist es, float immer schön nach double casten und char nach int.
Beim Keil C51 gibt es zwar einen Formatbezeichner für char-Zahlen, aber 
wenn man portabel sein will, besser nicht benutzen.
Die Ausgaben sind ja in der Regel für den langsamen Menschen, also muß 
man da nicht optimieren.

von Jim M. (turboj)


Lesenswert?

Adib schrieb:
> %f erwartet float, du hast double.

Wenn man keine Ahngung hat ... </Nuhrzitat>

Das %f würde auch mit double funktionieren - wenn man eine andere 
C-Library als Newlib verwendet. Die newlib braucht dafür leider relativ 
viel Speicherplatz auf dem Heap über malloc(), den man auf ARM Cortex-M 
eher selten hat. Aber es gäbe alternative sprintf() Implementationen.

Und %u nimmt auch uint16_t an...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fabian schrieb:
>
1
> sprintf("Vorher[%u] Nachher[%f]", a, (double)a);
2
>

Das sollte einen Fehler vom Compiler geben.

Entweder printf verwenden oder sprintf mit einen ausreichend großen 
Ausgabestring aufrufen:
1
sprintf (output_string, "Vorher[%u] Nachher[%f]", a, (double)a);

von fop (Gast)


Lesenswert?

Nennt sich glaube ich Optimierung. Nach der Umwandlung in 
Flieskommazahlen kräht kein Hahn mehr nach den Integervariablen. Daher 
wird deren Speicherplatz für andere schöne Werte benutzt. Der Debugger 
gibt Dir halt weiterhin den Wert aus der Speicherzelle aus, wo die 
Integervariable mal stand, als sie noch gebraucht wurde.
Es gibt zwar auch Versuche, dem Debugger Tips zu geben, ob eine Variable 
existiert. Die schießen aber unter Umständen über das Ziel hinaus, so 
dass Du einen Variableninhalt nicht angezeigt bekommst, obwohl die 
Variable noch existiert.

von Dr. Sommer (Gast)


Lesenswert?

Fabian schrieb:
> Compiler: ARM Yagarto
Ist der nicht völlig veraltet, mit dem letzten Update von 2012 und der 
GCC Version 4.7.2?

Von ARM selbst gibt es eine GCC Distribution der Version 6.3.1 und die 
wird laufend aktualisiert: 
https://developer.arm.com/open-source/gnu-toolchain/gnu-rm

von Fabian (Gast)


Lesenswert?

Dr. Sommer schrieb:
>> Compiler: ARM Yagarto
> Ist der nicht völlig veraltet, mit dem letzten Update von 2012 und der
> GCC Version 4.7.2?

Never touch a running system. :)

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.