Forum: Compiler & IDEs Modulo und uint32_t funktioniert nicht


von Jahat I. (jaib)


Lesenswert?

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
class Application : public Controller
2
{
3
    protected: uint16_t analogwert;
4
    protected: uint32_t spannung;
5
    protected: AnalogDevice poti;
6
    protected: Uart konsole;
7
    protected: String text;
8
 
9
    public: void onStart()
10
    {
11
        poti.config(0);
12
        konsole.config(9600);
13
    }
14
 
15
    public: void onWork()
16
    {
17
      // leer
18
    }
19
    
20
    public: void onTimer100ms()
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
class Application : public Controller
2
{
3
    protected: uint16_t analogwert, spannung;
4
    protected: AnalogDevice poti;
5
    protected: Uart konsole;
6
    protected: String text;
7
 
8
    public: void onStart()
9
    {
10
        poti.config(0);
11
        konsole.config(9600);
12
    }
13
 
14
    public: void onWork()
15
    {
16
      // leer
17
    }
18
    
19
    public: void onTimer100ms()
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);
25
        konsole.sendString(text);
26
    } 
27
} app;

was mache ich falsch? :/

Grüße Jahat

von Karl H. (kbuchegg)


Lesenswert?

> 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

von Klaus T. (gauchi)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

> spannung=analogwert*5000/1023;

ist ausserdem falsch. /1024 ist richtig.

Beitrag "Re: Berechnung zur Auflösung ADC - wie richtig?"

Der Überlauf ist ein klassischer Fehler, siehe

http://www.mikrocontroller.net/articles/Festkommaarithmetik#Die_L.C3.B6sung

MfG
Falk

von Jahat I. (jaib)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von raute (Gast)


Lesenswert?

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?

von Jahat I. (jaib)


Lesenswert?

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
class Application : public Controller
2
{
3
    protected: uint32_t analogwert;
4
    protected: uint32_t spannung;
5
    protected: AnalogDevice poti;
6
    protected: Uart konsole;
7
    protected: String text;
8
 
9
    public: void onStart()
10
    {
11
        poti.config(0);
12
        konsole.config(9600);
13
    }
14
    public: void onTimer100ms()
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.

von Falk B. (falk)


Lesenswert?

@  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));

MfG
Falk

von Jahat I. (jaib)


Lesenswert?

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
class Application : public Controller
2
{
3
    protected: uint32_t analogwert;
4
    protected: uint32_t rechnung;
5
    protected: uint16_t ausgabe;
6
    protected: AnalogDevice poti;
7
    protected: Uart konsole;
8
    protected: String text;
9
 
10
    public: void onStart()
11
    {
12
        poti.config(0);
13
        konsole.config(9600);
14
    }
15
    
16
    public: void onTimer100ms()
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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Jahat I. (jaib)


Lesenswert?

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
1
class Application : public Controller
2
{
3
    protected: uint32_t wert;
4
    protected: String text;
5
    protected: Uart konsole;
6
7
    public: void onStart()
8
    {
9
        konsole.config(9600);
10
        wert=0xFFFFFFFF;
11
    }
12
    
13
    public: void onWork()
14
    {
15
        text.format("32bit Wert = %u / %d / %X \n",wert,wert,wert);
16
        konsole.sendString(text);
17
        waitMs(100);
18
        wert--;
19
    } 
20
   
21
} app;

bringt dieses Ergebnis als Ausgabe:
1
32bit Wert = 65535 / -1 / FFFF 
2
32bit Wert = 65534 / -1 / FFFE 
3
32bit Wert = 65533 / -1 / FFFD 
4
32bit Wert = 65532 / -1 / FFFC 
5
32bit Wert = 65531 / -1 / FFFB 
6
32bit Wert = 65530 / -1 / FFFA 
7
32bit Wert = 65529 / -1 / FFF9 
8
32bit Wert = 65528 / -1 / FFF8 
9
32bit Wert = 65527 / -1 / FFF7 
10
32bit Wert = 65526 / -1 / FFF6 
11
32bit Wert = 65525 / -1 / FFF5 
12
32bit Wert = 65524 / -1 / FFF4 
13
32bit Wert = 65523 / -1 / FFF3 
14
32bit Wert = 65522 / -1 / FFF2 
15
32bit Wert = 65521 / -1 / FFF1 
16
32bit Wert = 65520 / -1 / FFF0 
17
32bit Wert = 65519 / -1 / FFEF 
18
32bit Wert = 65518 / -1 / FFEE 
19
32bit Wert = 65517 / -1 / FFED 
20
usw.

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

von Jahat I. (jaib)


Lesenswert?

OK... danke dir

Karl Heinz Buchegger schrieb:
> Gewöhn dir dieses Beschuldigen von Systemkomponenten ab.


Kopf waschen hat geholfen ... jetzt funtzt es tadelos:
1
class Application : public Controller
2
{
3
    protected: uint32_t analogwert, spannung;
4
    protected: AnalogDevice poti;
5
    protected: Uart konsole;
6
    protected: String text;
7
 
8
    public: void onStart()
9
    {
10
        poti.config(0);
11
        konsole.config(9600);
12
    }
13
 
14
    public: void onWork()
15
    {
16
      // leer
17
    }
18
    
19
    public: void onTimer100ms()
20
    {
21
        analogwert=poti.getValue10();
22
        spannung=analogwert*5000/1024; 
23
        text.format("Spannung = %ld,%03ld Volt \n",spannung/1000,spannung%1000);
24
        konsole.sendString(text);
25
    } 
26
} app;

und ich habs sogar begriffen :)

Danke Jahat

von Falk B. (falk)


Lesenswert?

@Jahat Iblis (jaib)

Es reicht, die Rechung in 32 Bit zu machen, die Variablen können 16 Bit 
bleiben.
1
class Application : public Controller
2
{
3
    protected: uint16_t analogwert, spannung;
4
    protected: AnalogDevice poti;
5
    protected: Uart konsole;
6
    protected: String text;
7
 
8
    public: void onStart()
9
    {
10
        poti.config(0);
11
        konsole.config(9600);
12
    }
13
 
14
    public: void onWork()
15
    {
16
      // leer
17
    }
18
    
19
    public: void onTimer100ms()
20
    {
21
        analogwert=poti.getValue10();
22
        spannung=(uint32_t)analogwert*5000L/1024L; 
23
        text.format("Spannung = %d,%03d Volt \n",spannung/1000,spannung%1000);
24
        konsole.sendString(text);
25
    } 
26
} app;

MFG
Falk

von (prx) A. K. (prx)


Lesenswert?

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;

von Karl H. (kbuchegg)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Jahat I. (jaib)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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".

von Rolf Magnus (Gast)


Lesenswert?

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.
1
text.format("Millivolt = %"PRIu32" \n", spannung);

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.