Forum: Compiler & IDEs Wann nimmt man welchen Zahlentyp?


von David P. (david_)


Lesenswert?

Hallo zusammen!

Kann mir jemand 'mal ein paar Tipps zu den Zahlentypen geben, die im 
AVR-GCC verwendet werden?
Ich habe beispielsweise gelesen, dass float und double beide 32 
Fließkommazahlen sind.

Gibt es irgendwo eine Tabelle oder etwas vergleichbares, in  der alle 
für den GCC möglichen (sinnvollen) Zahlentypen, ihre Bitlänge und ihre 
Eigenschaften drin stehen?

Ich habe ein kleines Programm zur ADC-Wandlung geschrieben und weiß nie 
genau, mit welchen Zahlentypen ich rechnen soll. Dieses Beispiel hier 
funktioniert glücklicherweise. Aber eher durch Zufall.
Hätte hier auch itoa funtioniert?:

char U[9];
char I[9];
uint16_t j;
for (j=0; j<800; j++)
  {
  dtostrf( (ErgebnisMatrix[j][0]/400.0)*1.9960, 8, 4, U );
        }

wobei ich vorher uint16_t volatile(ErgebnisMatrix[800][2]); deklariert 
habe.
Das Programm gibt später noch die Zahlen auf die serielle Schnittstelle 
aus. Deshalb die Wandlung.

Wäre sehr dankbar, um ein paar hilfreiche Tipps.

Beste Grüße

von Johannes M. (johnny-m)


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

David P. wrote:

> Ich habe ein kleines Programm zur ADC-Wandlung geschrieben und weiß nie
> genau, mit welchen Zahlentypen ich rechnen soll.

Du benutzt den Datentyp, der für deinen Bedarf gerade noch
ausreicht. Wobei auf einem AVR gilt: Wenn möglich vermeide
float oder double und versuche nur mit ganzen Zahlen auszukommen.
Wenn es denn unbedingt Kommazahlen sein müssen, dann geht auch
ein Trick den wir alle gerne benutzen:
Anstatt 3.45 Euro plus 2.87 Euro zu rechnen (das sind Kommazahlen),
kann man auch rechnen: 345 Cent plus 287 Cent (das sind jetzt
ganze Zahlen)

Hintergrund: Mit Fliesskommazahlen zu rechnen, ist für einen µC
sehr aufwändig wenn er keine FPU besitzt.

> Dieses Beispiel hier
> funktioniert glücklicherweise. Aber eher durch Zufall.
> Hätte hier auch itoa funtioniert?:

Kommt drauf an was du unter 'funktioniert' verstehst.
itoa -  i to a  -  integer to ascii
Wie der Name schon sagt, wandelt itoa einen int in seine ASCII
Reräsentierung um. Nun kann man natürlich auch aus den meisten
float einen Integer machen. Man verliert halt alle Nachkommastellen
und wenn der float vom Zahlenwert her zu gross ist, dann gibt es
ganz einfach keinen entsprechenden int dafür.

Auf einem AVR gilt

  char      8 Bit
  int      16 Bit
  long     32 Bit
  float    32 Bit
  double   32 Bit

die ersten 3 sind die Ganzzahltypen oder Integertypen. Die
lezteren beiden sind die Fliesskommatypen, wobei es beim gcc
auf einem AVR keinen Unterschied zwischen float und double
gibt (und man sie vermeiden möchte, wo nur geht)

Ich weis nicht was ich jetzt noch schreiben soll, es gibt noch
eine Unmenge über diese Basisdatentypen zu erzählen, aber vieles
davon ist sehr speziell und sprengt den Umfang eines Forums bei
weitem.

von Patrick (Gast)


Lesenswert?


von David P. (david_)


Lesenswert?

Was bedeutet denn, wenn hier jemand in einem AVR-PRogramm uint8_t oder 
uint16_t benutzt? ISt das genau das selbe wie das benutzen von char bzw. 
int ?
Wofür steht eigentlich dabei das _t ?

Da ich in meinem Array Zahlen vom Typ uint16_t benutzt habe, ist doch 
die Funktion dtostrf zur Umwandlung von double to string 
überdimensioniert, oder?
Ich hätte also atoi verwenden können.
Aber dann darf ich sicher keine Kommas mehr schreiben und müsste in 
millivolt rechnen, wenn ich das richtig verstanden habe. In meiner 
bisher funktionierenden Version war das Ergebnis, was auch der Seriellen 
Schnittstelle kam nämlich in Volt und schon als Kommazahl.

von Michael Wilhelm (Gast)


Lesenswert?

>Auf einem AVR gilt

>  char      8 Bit
>  int      16 Bit
>  long     32 Bit
>  float    32 Bit
>  double   32 Bit

Das ist so nicht richtig, sondern beim GCC. Der IAR-Compiler lässt auch 
64 Bit zu.

MW

von Robert (Gast)


Lesenswert?

Hi,

bleibt nur noch den

long long myvariable = 64 Bit lange Variable (auch unsigned möglich)

zu erwähnen.

Robert

von Karl H. (kbuchegg)


Lesenswert?

Michael Wilhelm wrote:
>>Auf einem AVR gilt
>
>>  char      8 Bit
>>  int      16 Bit
>>  long     32 Bit
>>  float    32 Bit
>>  double   32 Bit
>
> Das ist so nicht richtig, sondern beim GCC. Der IAR-Compiler lässt auch
> 64 Bit zu.

Schon richtig. Aber wir sind hier im GCC Forum :-)

von David P. (david_)


Lesenswert?

ok danke Michael. Aber ich benutze den GCC vom AVR-Studio.

Wo steckt in so einer Zahl denn das Vorzeichen?

von Robert (Gast)


Lesenswert?

Hi,

u am Anfang = unsigned = kein Vorzeichen
Zahl (z.B. 16) = Länge in Bits

Ansonsten: schau mal hier vorbei, da sind die Datentypen mit Ihren 
Wertebereichen aufgeführt:

http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html

Robert

von Karl H. (kbuchegg)


Lesenswert?

David P. wrote:
> Was bedeutet denn, wenn hier jemand in einem AVR-PRogramm uint8_t oder
> uint16_t benutzt? ISt das genau das selbe wie das benutzen von char bzw.
> int ?

uint8_t    u int 8 _t    unsigned
                         int
                         mit 8 Bit
                         Datentyp

uint16_t   u int 16 _t   unsigned
                         int
                         mit 16 Bit
                         Datentyp

> Wofür steht eigentlich dabei das _t ?

Als Kennung, dass es sich bei diesem Wort um einen Datentyp
handelt und nicht etwa um den Namen einer Variablen.

>
> Da ich in meinem Array Zahlen vom Typ uint16_t benutzt habe, ist doch
> die Funktion dtostrf zur Umwandlung von double to string
> überdimensioniert, oder?

In deinem Array stehen zwar uint16_t Werte. Das ist schon richtig.
Aber die gibst du ja nicht aus.
Was du ausgibst ist vielmehr das Ergebnis der Berechnung:

   (ErgebnisMatrix[j][0]/400.0)*1.9960

und da da durch 400.0 (beachte den Fliesskommapunkt) dividierst
und mit 1.9960 multiplizierst, ist das Ergebnis dieser Berechnung
kein uint16_t mehr sondern ein double.
Und um double in eine ASCII Form zu überführen, ist dtostrf()
ein mögliches Werkzeug.


> Ich hätte also atoi verwenden können.

Hättest du können. Nur wenn bei der Berechnung 2.34 herauskommt
und du auf der Ausgabe nur 2 siehst, wirst du nicht so glücklich
damit sein.

> Aber dann darf ich sicher keine Kommas mehr schreiben und müsste in
> millivolt rechnen, wenn ich das richtig verstanden habe. In meiner
> bisher funktionierenden Version war das Ergebnis, was auch der Seriellen
> Schnittstelle kam nämlich in Volt und schon als Kommazahl.

Genau.
Allerdings musst du dich ja nicht mit einer Ausgabe von
Millivolt zufrieden geben.
Mann kann ja immer noch die Zahl durch 1000 dividieren,
dann einen '.' ausgeben und anschliessend den Rest bei
einer Division durch 1000 ausgeben (allerdings die
führenden Nullen dabei nicht vergessen).

So wird dann aus 2387 Millivolt eine Ausgabe von
* zuerst die 2 (weil ja 2387 / 1000 genau 2 ergibt)
* dann ein '.'
* und zum Schluss den Rest bei der Division ( 2387 % 1000 ergibt 387)

und schon sieht dein Benutzer:   2.387
und denkt sich: Oha, 3 Nachkommastellen, wie praktisch!

von David P. (david_)


Lesenswert?

Danke Robert!

Aber leider finde ich diese Erklärungen von den .h dateien immer so 
furchtbar kompliziert.
Da steht dann beispielsweise

#define   INT8_MAX   0x7f
#define   INT8_MIN   (-INT8_MAX - 1)
#define   UINT8_MAX   (__CONCAT(INT8_MAX, U) * 2U + 1U)

Was sagt mir das alles?

Ich kenne ja .h Dateien. Aber die sehen immer viel übersichtlicher aus. 
Da sind dann einfach die ersten Zeilen meiner Funktionsdefinitionen 
drin. Aber hier in dieser Beschreibung stehen Dinge,mit langen 
Unterstrichen usw. Das versteh ich noch nicht so richtig.

von Karl H. (kbuchegg)


Lesenswert?

David P. wrote:
> Danke Robert!
>
> Aber leider finde ich diese Erklärungen von den .h dateien immer so
> furchtbar kompliziert.
> Da steht dann beispielsweise
>
> #define   INT8_MAX   0x7f
> #define   INT8_MIN   (-INT8_MAX - 1)
> #define   UINT8_MAX   (__CONCAT(INT8_MAX, U) * 2U + 1U)
>
> Was sagt mir das alles?
>
> Ich kenne ja .h Dateien. Aber die sehen immer viel übersichtlicher aus.
> Da sind dann einfach die ersten Zeilen meiner Funktionsdefinitionen
> drin. Aber hier in dieser Beschreibung stehen Dinge,mit langen
> Unterstrichen usw. Das versteh ich noch nicht so richtig.


Die rechten Teile der #define musst du fürs erste nicht verstehen

Was wird wohl ein INT8_MAX sein?
Welche Teile stecken da drinnen?
Da ist die Rede von  INT 8 und MAX
Also wird das wohl die maximale Zahl sein, die mit einem Integer
Datentyp mit 8 Bit gerade noch darstellbar ist.

Was wird dann UINT8_MAX sein?
U   wie unsigned
INT wie integer
8   wie 8 Bit
MAX wie Maximalwert

von David P. (david_)


Lesenswert?

Achso. Da stehen die Zahlenwerte in HEX angegeben. Ok. Also int8 geht 
von -128 bis 127

Aber das #define   UINT8_MAX   (__CONCAT(INT8_MAX, U) * 2U + 1U) ist 
wirklich etwas merkwürdig.

von Karl H. (kbuchegg)


Lesenswert?

David P. wrote:
> Achso. Da stehen die Zahlenwerte in HEX angegeben. Ok. Also int8 geht
> von -128 bis 127
>
> Aber das #define   UINT8_MAX   (__CONCAT(INT8_MAX, U) * 2U + 1U) ist
> wirklich etwas merkwürdig.

Sagte ichs schon: Das muss man fürs erste nicht verstehen :-)

Aber wenns interessiert:
Zerpflücken wir das Teil doch mal:
(__CONCAT(INT8_MAX, U) * 2U + 1U)

Das soll offensichtlich der Quelltext für eine Berechnung
sein. (Der Präprozessor rechnet nicht, der Präprozessor macht
Textersetzungen. Aber das Ergebnis von Textersetzungen kann
natürlich ein Text sein, der eine Formel darstellt, die vom
eigentlichen COmpiler ausgewertet wird).

Was haben wir denn da:
Da wird der Ausdruck __CONCAT(INT8_MAX, U)
mit 2 multipliziert und anschliessend noch 1 dazuaddiert.
Die U hinter jeder Zahl weisen die Zahl als unsigned Zahl
(also ohne Vorzeichen) aus, weil in C ja standardmaessig
eine ganze Zahl wie 2, 3 oder 1 ein int (also mit Vorzeichen)
ist.

Was macht __CONCAT
Da alle Buchstaben Grossbuchstaben sind, ist das nach üblicher
C Konvention selbst wieder ein Makro. Es gibt also irgendwo
einen #define dafür. Der Name CONCAT deutet darauf hin, dass
hier Teile (Texte) miteinander verbunden werden. Welche Texte?
Nun, der Text für INT8_MAX und U.
INT8_MAX ist auch wieder ein Makro. Dessen Ersetzung ergibt
0x7F. Da dann ein U drann gehängt ergibt 0x7FU
Damit ergibt die erste Runde der Textersetzungen

Das hier:
(__CONCAT(INT8_MAX, U) * 2U + 1U)
wird zu
( 0x7FU * 2U + 1U )
und wenn ich das mal ausrechne, dann ergibt sich

( 127 * 2 + 1 )

oder auch

(255)

und das ist rein 'zufällig* die größte Zahl die mit 8 Bit
als unsigned noch darstellbar ist.

von David P. (david_)


Lesenswert?

Hi Karl Heinz!

Danke für die Mühe! Das war sehr hilfreich!!!

von Michael Wilhelm (Gast)


Lesenswert?

@ Karl-Heinz,

wohl wahr, dass wir im GCC-Forum sind. Deine Formulierung klang, als ob 
der AVR nicht 64-Bit Rechnungen kann. Und das wollte ich korrigieren.

MW

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.