Hallo,
ich habe folgendes Problem. Bei einer Stringformatierung verwende ich
zum ermitteln der Nachkommastellen den Modulo-Operator. Das funktioniert
auch soweit ganz prima mit uint16_t- Werten nur sobald ich das gleiche
mit uint32_t-Werten mache erhalte ich vom Modulo immer eine Null :-(
Beispiel wo es nicht funktioniert:
1
classApplication:publicController
2
{
3
protected:uint16_tanalogwert;
4
protected:uint32_tspannung;
5
protected:AnalogDevicepoti;
6
protected:Uartkonsole;
7
protected:Stringtext;
8
9
public:voidonStart()
10
{
11
poti.config(0);
12
konsole.config(9600);
13
}
14
15
public:voidonWork()
16
{
17
// leer
18
}
19
20
public:voidonTimer100ms()
21
{
22
analogwert=poti.getValue10();
23
//Volt=analogwert*VRef/Maximalwert
24
spannung=analogwert*5000/1023;
25
text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000);
26
konsole.sendString(text);
27
}
28
}app;
und hier das gleiche mit uint16_t was absolut prima läuft:
1
classApplication:publicController
2
{
3
protected:uint16_tanalogwert,spannung;
4
protected:AnalogDevicepoti;
5
protected:Uartkonsole;
6
protected:Stringtext;
7
8
public:voidonStart()
9
{
10
poti.config(0);
11
konsole.config(9600);
12
}
13
14
public:voidonWork()
15
{
16
// leer
17
}
18
19
public:voidonTimer100ms()
20
{
21
analogwert=poti.getValue10();
22
//Volt=analogwert*VRef/Maximalwert
23
spannung=analogwert*50/1023;
24
text.format("Spannung = %d,%d Volt \n",spannung/10,spannung%10);
> spannung=analogwert*5000/1023;
Sobald dein analogwert größer als 6 ist, gibt das einen satten Überlauf.
6 * 5000 -> 30000 passt gerade noch in einen 16 Bit int
7 * 5000 -> 35000 passt nicht mehr in einen 16 Bit int
Jahat Iblis schrieb:> text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000);
bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist
bei einem Integer eigentlich nicht sinnvoll. Ich würde da jetzt %03d
schreiben.
Karl Heinz Buchegger schrieb:>> spannung=analogwert*5000/1023;>> Sobald dein analogwert größer als 6 ist, gibt das einen satten Überlauf.> 6 * 5000 -> 30000 passt gerade noch in einen 16 Bit int> 7 * 5000 -> 35000 passt nicht mehr in einen 16 Bit int
naja aber das Attribut spannung ist ja 32 bit und da sollte das Ergebnis
locker rein passen ... ich schau mir noch mal den Artikel zu den
Festkommazahlen an... danke erst mal.
Klaus T. schrieb:>> text.format("Spannung = %d,%.3d Volt \n",spannung/1000,spannung%1000);>> bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist> bei einem Integer eigentlich nicht sinnvoll. Ich würde da jetzt %03d> schreiben.
jup ... war ein Irrtum vom Amt sollte natürlich %03d heißen... ist ja
keine Gleitkommazahl ;-)
Gruß Jahat
Jahat Iblis schrieb:> naja aber das Attribut spannung ist ja 32 bit und da sollte das Ergebnis> locker rein passen ...
Standarddenkfehler. In a=b*c wird b*c unabhängig vom Typ von a
berechnet.
Klaus T. schrieb:> bist du dir bei dem Formatstring ganz sicher? Die Präzisionsangabe ist> bei einem Integer eigentlich nicht sinnvoll.
Doch.
> Ich würde da jetzt %03d schreiben.
Hat effektiv die gleiche Wirkung.
Jahat Iblis schrieb:> ..> protected: String text;> ..> text.format("Spannung = %d,%d Volt \n",spannung/10,spannung%10);
In welcher String-Klasse gibt es den die Methode format?
ok, das mit dem Überlauf war mir schon irgendwie klar deshalb hab ich ja
das Ergebnis auf 32Bit dimensioniert. Da aber offensichtlich auch kein
16Bit-Wert in der Rechnung selber sein darf hab ich jetzt mal alle Werte
als 32 Bit deklariert und Kontrollausgaben der Zwischenergebnisse
gemacht... es schaut aus als wenn Modulo bei uint32_t nicht
funktioniert:
1
classApplication:publicController
2
{
3
protected:uint32_tanalogwert;
4
protected:uint32_tspannung;
5
protected:AnalogDevicepoti;
6
protected:Uartkonsole;
7
protected:Stringtext;
8
9
public:voidonStart()
10
{
11
poti.config(0);
12
konsole.config(9600);
13
}
14
public:voidonTimer100ms()
15
{
16
analogwert=poti.getValue10();
17
text.format("Analogwert = %d \n",analogwert);
18
konsole.sendString(text);
19
spannung=analogwert*5000/1024;
20
text.format("Millivolt = %d \n",spannung);
21
konsole.sendString(text);
22
text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000);
23
konsole.sendString(text);
24
}
25
}app;
Ausgabe:
1
Analogwert = 1022
2
Millivolt = 4995
3
Spannung = 4,000
4
Analogwert = 914
5
Millivolt = 4467
6
Spannung = 4,000 Volt
7
Analogwert = 790
8
Millivolt = 3861
9
Spannung = 3,000 Volt
10
Analogwert = 666
11
Millivolt = 3255
12
Spannung = 3,000
13
... usw.
oder an welcher Stelle bin ich zu dusselig? In der Regel sitzt der
Fehler ja immer vor der Tastatur :(
Grüße Jahat
Ach so der String ist irgenwie bei SiSy mit drin, bin dabei mich durch
das AVR-C++ Tutorial zu kämpfen.
@ Jahat Iblis (jaib)
>das Ergebnis auf 32Bit dimensioniert.
reicht nicht.
> Da aber offensichtlich auch kein>16Bit-Wert in der Rechnung selber sein darf
Doch, aber mindestens EINER muss 32 Bit sein, bei größeren Formeln, die
der COmpiler häppchenweise bearbeitet ggf. mehrere! Im Zweifelsfall alle
Eingangsvariablen in der Formel auf 32 Bit casten.
>gemacht... es schaut aus als wenn Modulo bei uint32_t nicht>funktioniert:
Glaub ich nicht.
>oder an welcher Stelle bin ich zu dusselig? In der Regel sitzt der>Fehler ja immer vor der Tastatur :(
1
text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000);
Ich glaub hier kann es knirschen, denn %d bezieht sich auf einen
Integer, nicht long int. Eher so.
1
text.format("Spannung = %d,%03d Volt \n",spannung/1000,(uint16_t)(spannung%1000));
alles 32bit und casting beim format auf 16bit hab ich schon probiert...
geht nicht und die folgende Zeile gibt ja den 32bit wert korrekt aus..
Jahat Iblis schrieb:> text.format("Millivolt = %d \n",spannung);
also funktioniert %d mit uint32_t.
wieso sollten 32 bit nicht reichen?
1023 * 5000 = 5115000 = 0x4E0C78 ... größer wird da ja nix
übrigens so funktioniert es... aber eben Modulo auf 16 Bit
1
classApplication:publicController
2
{
3
protected:uint32_tanalogwert;
4
protected:uint32_trechnung;
5
protected:uint16_tausgabe;
6
protected:AnalogDevicepoti;
7
protected:Uartkonsole;
8
protected:Stringtext;
9
10
public:voidonStart()
11
{
12
poti.config(0);
13
konsole.config(9600);
14
}
15
16
public:voidonTimer100ms()
17
{
18
analogwert=poti.getValue10();
19
rechnung=analogwert*5000/1023;
20
ausgabe=(uint16_t)rechnung;
21
text.format("Spannung = %d,%03d Volt \n",ausgabe/1000,ausgabe%1000);
22
konsole.sendString(text);
23
}
24
25
}app;
Das muss ich irgendwie erst mal verdauen... hab aber keinen plan wie ich
jetzt mal feststelle ob Modulo mit uint32_t geht :/
Gruß Jahat
Wenn Spannung ein uint32_t ist, dann kann es hier
text.format("Spannung = %d,%03d Volt \n",spannung/1000
nicht %d im Format String heissen.
%d ist für int. Du hast aber keinen int, du hast mit 32 Bit auf deinem
System offensichtlich einen long. Also muss es zumindest %ld heissen.
(UNd das das ganze eigentlich unsigned ist, lass ich jetzt mal mit einem
Augenzwinkern unter den Tisch fallen)
> jetzt mal feststelle ob Modulo mit uint32_t geht :/
Du bist immer noch am falschen Dampfer.
Gewöhn dir dieses Beschuldigen von Systemkomponenten ab. Zu 99% sitzt
das Problem immer vor den Bildschirm und es ist DEIN Programm, welches
fehlerhaft ist.
hm... ok... wenn nicht %d was dann? :/ das Ergebnis der Operation passt
definitiv in 16 Bit und hat so oder so nimmer ein Vorzeichen, die
Division funktioniert problemlos so wie ich es gebaut hab ... :/
übrigens geh ich strikt davon aus, dass ICH was falsch mache nur versteh
ich es noch nicht was :/ ... es scheint dann wohl doch beim format zu
liegen der folgende Code
ich muss dir also recht geben Karl Heinz die Formatanweisungen %u %d und
%x beziehen sich auf 16Bit Werte ... jetzt muss ich nur noch mals das
Modulo checken...
Gruß Jahat
OK... danke dir
Karl Heinz Buchegger schrieb:> Gewöhn dir dieses Beschuldigen von Systemkomponenten ab.
Kopf waschen hat geholfen ... jetzt funtzt es tadelos:
1
classApplication:publicController
2
{
3
protected:uint32_tanalogwert,spannung;
4
protected:AnalogDevicepoti;
5
protected:Uartkonsole;
6
protected:Stringtext;
7
8
public:voidonStart()
9
{
10
poti.config(0);
11
konsole.config(9600);
12
}
13
14
public:voidonWork()
15
{
16
// leer
17
}
18
19
public:voidonTimer100ms()
20
{
21
analogwert=poti.getValue10();
22
spannung=analogwert*5000/1024;
23
text.format("Spannung = %ld,%03ld Volt \n",spannung/1000,spannung%1000);
Falk Brunner schrieb:> spannung=(uint32_t)analogwert*5000L/1024L;
Ist zwar technisch egal, aber schöner sieht es so aus:
spannung=(uint32_t)(analogwert*5000L)/1024L;
Jahat Iblis schrieb:> und ich habs sogar begriffen :)
OK. Du hast begriffen was die Ursache war.
Aber hast du auch begriffen, wie die Ausgabe die du gesehen hast, zu
Stande gekommen ist?
Ich geh da jetzt mal näher drauf ein, weil diese Analyse sehr erhellend
ist und verdeutlicht, warum es so wichtig ist, dass die
Formatieranweisungen IMMER zum Datentyp des angegebenen Ausdrucks passen
müssen.
* printf ist eine variadische Funktion. Deine .format ist da auch nicht
anders. Kann sie auch gar nicht sein.
* Das wiederrum bedeutet, dass sich der Compiler darauf verlassen muss,
dass die aufgerufene Funktion die Argumente so vom Stack holt,
wie sie der Aufrufer hinaufschreibt.
* Als variadische Funktion hat printf (und .format) keine Möglichkeit
festzustellen, was der Aufrufer auf den Stack gelegt hat. Der
Mechanismus, der normalerweise da Ordnung reinbringt greift hier
nicht. Das einzige was printf zur Verfügung hat, ist der Formatstring.
* Der Compiler weiß, dass ein int 2 Byte hat und ein long 4 Bytes
* Auch printf weiß, dass ein int 2 Bytes hat und ein long 4.
Was passiert beim Aufrufer?
Der Aufrufer legt die beiden numerischen Argumente auf den Stack. Diese
sind 32 Bit Argumente. Also werden pro Argument 4 Bytes auf dem Stack
abgelegt. Auf deinem Stack liegen also 8 Bytes (in einer spezifischen
Reihenfolge, die hier wichtig ist und dafür sorgt, dass du die 0 gesehen
hast uns hier aber nicht weiter interessieren soll.)
A0 A1 A2 A3 B0 B1 B2 B3
(A ist die erste 32-Bit Zahl. B ist die andere 32-Bit Zahl. Die Nummer
ist jeweils die Bytenummer.)
Das ist das was der Aufrufer an .format weitergibt.
A0 A1 A2 A3 B0 B1 B2 B3
|
v
0 1 2 3 4 5 6 7
.format weiß aber nichts von dieser Organisation. Alles was es hat ist
der Formatstring und einen Stack auf dem 8 Bytes liegen. Den
Formatstring zerlegt es sich erst mal. Dabei stösst es auf das %d. Da
.format die Regeln kennt, weiß es, dass mit %d ein int verknüpft ist.
int bedeutet: 2 Bytes. .format holt sich also die ersten beiden Bytes
vom Stack und interpretiert diese als int.
0 1 2 3 4 5 6 7
| |
v v
A0 A1
dieser int wird entsprechend aufbereitet und ausgegeben. Danach geht es
weiter im Format-String und .format findet die nächste Angabe: wieder
ein %d. Also holt es sich die nächsten beiden Bytes (%d ist ja ein int)
vom Stack und interpretiert diese wiederrum als int
0 1 2 3 4 5 6 7
| |
v v
B0 B1
Das wars. Im Formatstring sind keine weiteren %-Angaben mehr enthalten,
woraus .format den Schluss zieht, dass es fertig ist und der
Ausgabestring somit um die Argumente ergänzt wurde.
Aber:
Wenn du dir das ansiehst, dann wurden für die 2-te Zahl ganz andere
Bytes zur Ausgabe herangezogen als der Aufrufer beabsichtigte!
Der Aufrufer wollte die beiden im Formatstring angegebenen Argumente in
ihrer Bytezerlegung so aufgefasst wissen
A0 A1 A2 A3 B0 B1 B2 B3
+--------+ +--------+
durch den falschen Formatstring hat .format aber auf dem Stack das hier
gesehen
A0 A1 B0 B1
+--+ +--+
und damit die Bytes auf dem Stack völlig falsch den Argumenten
zugeordnet.
Und deshalb ist es wichtig, dass die Angaben im Formatstring mit den
tatsächlichen Datentypen der angegebenen Ausdrücke übereinstimmt! Tut es
das nicht, dann passieren solche Dinge.
A. K. schrieb:> Ist zwar technisch egal, aber schöner sieht es so aus:> spannung=(uint32_t)(analogwert*5000L)/1024L;
Besser: spannung = (analogwert * 5000UL) / 1024UL;
oder spannung = ((uint32_t)analogwert * 5000) / 1024;
Nach uint32_t (unsigned) zu casten und mit 5000L (signed) zu
multiplizieren ist eher verwirrend als hilfreich.
hm... ok das muss ich mir in Ruhe durch den Kopf gehen lassen.
Ansonsten danke für die schnelle Hilfe und ich werde mich bestimmt bald
mit meinem nächsten Problem hier melden :)
Gruß Jahat
A. K. schrieb:> Nach uint32_t (unsigned) zu casten und mit 5000L (signed) zu> multiplizieren ist eher verwirrend als hilfreich.
Es ist allgemein keine gute Idee, signed und unsigned in einer Rechnung
zu mischen. GCC bringt bei sowas auch gerne mal eine Warung.
Jahat Iblis schrieb:> Jahat Iblis schrieb:>> text.format("Millivolt = %d \n",spannung);>> also funktioniert %d mit uint32_t.
Nein!
Versuche nicht, durch Trial-and-Error eine Sprache zu lernen und falsche
Schlussfolgerungen zu ziehen wie
"Code macht was er soll" => "Code ist korrekt".
avr-gcc übergibt Argumente an varargs-Funktionen auf dem Stack,
high-Byte zuerst. Dementsprechend werden die Argumente low-zuerst
gelesen.
Ein "%d" liest also nur 2 Bytes vom Stack, und zwar die low-Bytes und
zeigt diese wie erwartet an. Es bleiben aber 2 nicht-gelesene
high-Bytes.
Das alles funktioniert nur deshalb und fliegt dir nicht um die Ohren,
weil mit avr-gcc der Callee die Argumente über den Framepointer liest
und nicht per POP.
Folgt aber ein weiteres Argument nach dem uint32_t, hast du den
Schamassel und suchst dir den Wolf, weil "uint32_t mit %d nachweislich
funktioniert".
Wenn man sicher und allgemein den richten Format-Specifier will, muß man
für die (u)int*_t-Typen zu den entsprechend dafür definierten Makros
greifen. Die sind nicht so wirklich schön, aber wenigstens in jedem Fall
korrekt.