mikrocontroller.net

Forum: Compiler & IDEs Vergleichsoperatoren und Typen abhängig?


Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute.
Ein neues Ärgernis für das mir die Erklärung fehlt. Die folgende
Routine  soll die vom Controller verwendete ID als Zahl im Wertebereich
zwischen 0 und 8191 verändern, was sie auch ganz prima macht, solange
Controller_ID vom Typ float ist.
Sobald ich Controller_ID als unsigned int deklariere geht nichts mehr.
Die Obergrenze funktioniert dann noch aber die Zahl kann kleiner als 0
als z.B.-167 werden. Sofern sie aber kleiner als 0 ist, läßt sie sich
nicht mehr vergrößern, wenn das Ergebnis der Addition kleiner als 0
werden würde.
Aktivere ich die beiden auskommentierten Zeilen, springt die Zahl nach
0 auf 8191, nach 8191 aber nicht zurück auf 0.
Obschon die Zahl, sofern über 0 eingestellt, dann im Programm richtig
funktioniert, also auch als z.B. 15 verwendet wird, steht etwas ganz
anderes im EEprom und wird nach einem Reset als 99 ausgelesen.
Woran liegt das?
Wie umgeht man das?


void adjust_controller_ID (void)
   {
  int stelle = 1;
  float temp_Controller_ID = Controller_ID;

  for (;Key != 5;)
    {
    readkey();
    switch (Key)
      {
      case 1:
      if( stelle < 1000)  stelle = stelle * 10;
      Key = 0;
      break;
      case 2:
      if( stelle > 1 )  stelle = stelle / 10;
      Key = 0;
      break;
      case 3:
      if ((temp_Controller_ID + stelle) <= 8191.0) temp_Controller_ID =
temp_Controller_ID + stelle;
      Key = 0;
      break;
      case 4:
      if ((temp_Controller_ID - stelle) >= 0.0 ) temp_Controller_ID =
temp_Controller_ID - stelle;
      Key = 0;
      break;
      }
//    if (temp_Controller_ID > 8191) temp_Controller_ID = 8191;
//    if (temp_Controller_ID < 0) temp_Controller_ID = 0;
    display_cursor(2,1);
    sprintf(displaystring,"ID. %04d", (unsigned
int)temp_Controller_ID);
    display_string(displaystring);
    }
  Controller_ID=temp_Controller_ID;
  eeprom_write_block(&Controller_ID,(void *)0x0080,sizeof
Controller_ID);
  Key = 0;
  }

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also entweder hab ich Dich falsch verstanden, oder:

>if (temp_Controller_ID > 8191) temp_Controller_ID = 8191;
>if (temp_Controller_ID < 0) temp_Controller_ID = 0;

muss:

/*
 * Erzeuge "Überlauf" bei überschreiten des
 * gültigen Wertebereiches an der Obergrenze
 * von '8191' (0 .. 8191 .. 0)
 */
if (temp_Controller_ID > 8191) temp_Controller_ID = 0;

/*
 * Erzeuge "Überlauf" bei unterschreiten des
 * gültigen Wertebereiches an der Untergrenze
 * von '0' (8191 .. 0 .. 8191)
 */
if (temp_Controller_ID < 0) temp_Controller_ID = 8191;

...manchmal können Kommentare gar nicht mal so falsch sein ;-)

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nein, nein, die if-Abfrage ist die letzte Bastion die dem Wahnsinn
Einhalt gebieten soll.
Wenn temp_Controller_ID größer als 8191 geworden ist soll sie zurück
auf 8191 gesetzt werden und wenn sie kleiner als 0 wurde, soll sie
zurück auf Null gestetzt werden. Beide Zustände dürften theoretisch ja
gar nicht auftreten, und sie tun es ja auch nicht, wenn
temp_Controller_ID vom Typ float ist, aber es geschieht, wenn
temp_Controller_ID von Typ int oder unsigned int ist.
Die Anweisung

if ((temp_Controller_ID + stelle) <= 8191.0) temp_Controller_ID =
temp_Controller_ID + stelle;

soll doch bewirken, dass die Zahl "Stelle" nur dann zu
temp_Controller_ID hinzuaddiert oder subtrahiert wird, wenn das
Ergebnis dieser Berechnung nicht größer als 8191 und (nächste Zeile)
nicht kleiner als 0 wird.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, wird 'stelle' möglicherweise irgendwo in einem Interrupt
modifiziert?

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nein.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> if ((temp_Controller_ID - stelle) >= 0.0 )
>         temp_Controller_ID = temp_Controller_ID - stelle;

Siehe C Handbuch, zu Datentypen. Da "unsigned int" und "int" gleich
gross sind, ist das Resultat von "ID - stelle" vom Type "unsigned
int". Der Vergleich ist daher unsinnig. Müsste der Compiler auch
mitgeteilt haben, der GCC jedenfalls merkt sowas.

Autor: Rufus T. Firefly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie kann temp_Controller_ID vom Typ "unsigned int" sein und kleiner
werden als 0?

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Sobald ich Controller_ID als unsigned int deklariere[...]

So kann es zu "unsigned int" werden. Aber kleiner als Null kann es so
natürlich nicht werden.

Zur Frage im Titel: Die Implementierung von Vergleichsoperatoren ist
sehr wohl typenabhängig. Der Maschinencode von if(a>b) ist verschieden,
je nachdem ob a,b vom Typ "int" oder "unsigned int" sind. Und jeder
Compiler kann daher wissen, dass if(a>=0) bei "unsigned int" Blödsinn
nichts anderes ist als if(1), weil a per Definition nicht negativ sind
kann.

Autor: OldBug (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Naja, hier mal ein kleines Programm für den PC. Ich habe bewusst die
Datentypen aus stdint.h verwendet, damit mir nachher niemand mit dem
Argument kommt, "auf nem AVR sind die Typen aber kleiner"...

$ gcc -O -ggdb -o ueberlauf.exe ueberlauf.c

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
der Vergleich mit 0.0 steht darum da drin, weil es mit Int nicht
funktioniert und die Deklaration darum unsinniger Weise als float
erfolgt, wil es damit zumindest funktioniert, daum ist dann auch der
Vergleich eine Float. Mit Int steht da natürlich 0.

@Rufus
<< Wie kann temp_Controller_ID vom Typ "unsigned int" sein und
kleiner
werden als 0?
Genau das ist die Frage.

@ A.K
<< Aber kleiner als Null kann es so natürlich nicht werden.
Soll es ja auch garnicht, wird es aber trotzdem.

<< dass if(a>=0) bei "unsigned int" Blödsinn ...
Du meinst es ginge, wenn statt 0 (temp_Controller_ID - stelle) >= 1
stünde? Habe ich auch probiert, geht nicht, wird trotzdem negativ

Autor: OldBug (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
>Habe ich auch probiert, geht nicht, wird trotzdem negativ

Das liegt aber nicht am Programm, siehe Anhang!

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Mit Int steht da natürlich 0."

Was nichts an der Problematik ändert. Denn ob da mit "a" als
"unsigned int" nun if(a>=0.0) oder if(a>=0) steht ist egal, beides
ist effektiv identisch mit if(1).

"Soll es ja auch garnicht, wird es aber trotzdem."

Wie soll das gehen? Ein Compiler der bei
  unsigned int a;
  if (a < 0) {
     ...
  }
jemals den Code im if-Block ausführt gehört definitiv in die Tonne
gekloppt. Ein Typ ohne Vorzeichen hat nun einmal kein Vorzeichen, kann
also prinzipbedingt nicht negativ werden.

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Oldbug
Mit der PC-Version hab ich es noch nicht probiert, nur gestern mit dem
Controller, aber ich werde es heute abend gerne nochmal ausprobieren.
Allerdings tut sich damit dann auch wieder das EEProm Problem auf und
das kann unmöglich mit der 0 Zusammenhängen weil es ja bei positiven
Controller_ID-Werten zuschlägt.
Gibt es da auch noch einen Tip?

Autor: OldBug (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Naja, keine Ahnung, woran das liegen könnte.
Ich müsste mal den relevanten Code für das Auslesen der ID sehen,
denn beim schreiben sieht alles recht gut aus.
Ich habe mal eine "Dummy-Funktion" in das PC-Progrqamm mit eingebaut,
welches das eeprom-Schreiben Simuliert.

Autor: OldBug (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
...hier der Output auf dem PC...

Autor: tex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich liefere es morgen früh zusammen mit dem EEProm Inhalt.
Erst mal ganz herzlichen Dank für die Hilfe

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hat da jemand Identitätsprobleme?

  :-)

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vermutlich mehr ein Kompetenzproblem.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie meinen?

Autor: tex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,
Glaubt es, oder glaubt es nicht, das Ergebnis kann negativ werden, wenn
die Variable als unsigned int deklariert ist und wird nicht negativ,
wenn sie als int deklariert ist.

Und jetzt kommts. Wenn ich die als int deklarierte Variable in der
Funktion zum auslesen des EEproms als extern float deklariere, klappt
auch das auslesen.

extern float Controller_ID;
  if(!(eeprom_read_byte((void *)0x0080)==0xff))
    {
    eeprom_read_block(&speicher,(void *)0x0080,sizeof interval );
    Controller_ID = (float)speicher;
    }
Watt n Scheiß!
@OldBug
Keine Persönlichkeitsspaltung nur Arbeitsteilung. Ich darf die Software
machen und die faule Sau ist fliegen gegangen.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Glaubt es, oder glaubt es nicht, das Ergebnis kann negativ werden,
wenn die Variable als unsigned int deklariert ist"

Eine 16-Bit Variable "unsigned int" hat den Wertebereich 0..65535.
Siehst Du da darin irgendeine negative Zahl? Ich nicht. Wenn Du von 0
eins abziehst, kriegst Du 65535 und keinen negative Wert.

Erst wenn du sowas wieder in "int" konvertierst oder z.B. mit
printf("%d",...) ausgibst taucht ein Vorzeichen auf. Das ist dann
aber nicht wirklich sinnvoller als beispielsweise der Schrott, der bei
printf("%d", 3.1415) rauskommt.

@OldBug: Sowas ist natürlich der beste Weg, solche Probleme zu
kriegen:
   printf("ID. %04d\n", (uint16_t)temp_Controller_ID);
denn %d ist nur für "int" definiert. Hier sollte wohl "%u" stehen.
Übrigens ist noch ein weiter Fehler drin, denn "%d" gilt für "int",
nicht für "int16_t", passt nur wenn sizeof(int16_t) <= sizeof(int),
was immerhin in allen ANSI-Fällen klappt, aber beispielsweise nicht bei
dem unter µC-Compilern nicht so seltenen Fall sizeof(int)=1. Generell
ist in printf() mit den stdint.h-Typen nichts sinnvolles zu holen.

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
Das die Zahl theoretisch nicht negativ werden darf, weiß ich ja, ich
möchte nur eines wissen: wenn die Zahl 0 erreicht hat (unsigned int)
dürfte sie sich doch bei weiterer Subtraktion nicht mehr verändern oder
anders herum, die If-Abfrage müßte doch verhindern, das weitere
Subtraktionen statt finden. Tut sie aber nicht. Die Zahl wird negativ.
Noch besser, sie kann nur dann erhöht werden, wenn das Erbenis der
addition eine positive zahl ist. Wäre die zahl -13 und sollte um 1 oder
10 erhöht werden funktioniert es nicht eine Erhöhung um 100 fuktioniert.
Subtrahieren läßt sich im negativen Bereich aber alles.
Wenn wir voraussetzen, dass nur die Anzeige falsch ist, dürfte sich die
Anzeige aber nicht weiter verändern?
Ich glaube dass der Fehler im Handling der Variablen liegt. Die
Untergrenze wird generell ignoriert, egal ob es nun 0, 1, oder 100 ist,
wenn das Ergebnis der If-Abfrage in den negativen Bereich geht. Ist das
Ergebnis der If-Abfrage positiv, funktioniert die Abfrage.

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast 2 unsigned Zahlen und willst sie maximal voneinander abziehen
ohne das ein Wrap-Around erfolgt:

unsignet int x, y;

if( x >= y )
  x -= y;
else
  x = 0;


Peter

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Die Zahl wird negativ."

Die Zahl nicht. Nur das was Du vor ihr siehst.

Was gibt
  printf("%d", 4000);
aus? Eine negativen Wert. Ist 4000 negativ? Nein. Also versuch mir
bitte nicht zu erklären, dass eine vorzeichenlose Variable einen
negativen Wert annehmen kann. Damit bringst Du nur dich selber völlig
durcheinander.

Mit printf("%d") eine Variable vom Typ "unsigned int" auszugeben
wird immer eine irreführende Anzeige produzieren, sobald die (16-Bit)
Variable grösser ist als 32767.

"wenn das Ergebnis der If-Abfrage in den negativen Bereich geht"

Das ist ja grad das Missverständnis. Die Statements
     unsigned int temp_Controller_ID = ...;
     if ((temp_Controller_ID - stelle) >= 0 )
             temp_Controller_ID = temp_Controller_ID - stelle;
sind in ihrer Wirkung exakt identisch mit:
     unsigned int temp_Controller_ID = ...;
     if (1)
             temp_Controller_ID = temp_Controller_ID - stelle;

Der Versuch, mit dem if-Statement dich vor vermeintlich "negativen"
Werten zu schützen, muss hier fehlschlagen.

Der Grund für diese Eigenschaft des if-Statements:

Bei Operationen der gleich grosser Datentypen "unsigned int" und
"int" ist das Ergebnis "unsigned". Also der Datentyp von (ID -
stelle) ist immer positiv (JA!!!!). Was für Werte temp_Controller_ID
und stelle auch immer haben mögen, die Maschine wird die if-Anweisung
nie abweisen.

Was Du wohl erreichen willst, schreibt sich
     if (temp_Controller_ID > stelle)
             temp_Controller_ID = temp_Controller_ID - stelle;
oder du verwendest "int" statt "unsigned int" als Typ von
temp_Controller_ID.

So, ich hoffe das war's jetzt. Wenn nicht kann ich dir nur dringend
raten, Assembler, C und andere Compiler weiträumig zu umgehen und dich
auf Basic zu beschränken.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Was gibt
  printf("%d", 4000);"

Sorry, da fehlt eine 0. Sollte
  printf("%d", 40000);
heisse.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.:
Ja, sorry. Ich habe noch überlegt, ob ich das erwähnen sollte, aber das
hatte ich jetzt einfach mal vorausgesetzt. Offensichtlich ein grober
Schnitzer ;)

Worauf ich hinauswollte: es wird sich sicher nur um einen
Anzeigefehler handeln...

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@OldBug: Der Witz ist ja grad, dass dieser (ja, kleine) Fehler im
Original von flyingwolf nicht drin ist, sondern erst bei dir ;-). Bei
ihm steht (unsigned int), was ja ok ist, nur das %d passt nicht dazu.

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist kaum von der Hand zu weisen, das A.K.s Version am plausibelsten
klingt. Im Prinzip hat Peter das gleiche geschrieben nur so klasse
komprimiert, das ich eine Weile gebraucht habe um mitzubekommen wo
jetzt der feine Unterschied liegt. Ich denke wir können das Problem als
gelöst betrachten. Nochmal Danke an Alle.
@A.K.
Mit Basic konnte ich mich noch nie anfreunden.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.:

Jetzt kann ich Dir nicht mehr folgen :\

[Quote flyingwolf]
sprintf(displaystring,"ID. %04d", (unsigned
int)temp_Controller_ID);
[/Quote]

[Quote ueberlauf.c]
//sprintf(displaystring,"ID. %04d", (unsigned
int)temp_Controller_ID);
//display_string(displaystring);
printf("ID. %04d\n", (uint16_t)temp_Controller_ID);
[/Quote]

Der einzige Unterschied, den ich da sehe, ist zwischen 'unsigned int'
und 'uint16_t', was ich aber absichtlich gemacht habe, weil ich die
Breite von 'unsigned int' auf dem Controller ebenfalls auf dem PC
haben wollte...

confused

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@OldBug: ok, Frieden :-), ich werde versuchen die Haare nicht noch
weiter zu spalten.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich denke, ich weis mittlerweile, worauf Du hinauswolltest:
Wenn man den von mir bereitgestellten Code auf einem Mikrocontroller
mit sizeof(int) = 1 laufen lassen würde, wäre %d als Formatstring zu
klein für uint16_t. Das war mir natürlich bewusst ;)
Es ging mir darum, die 32Bit breiten Integer auf dem PC zu vermeiden,
weil sonst mit sicherheit irgendjemand auf die Idee gekommen wäre, zu
behaupten, daß das ja nur Funktioniert, weil auf dem Mikrocontroller
nur 16 Bits zur Verfügung stehen.

Btw: ich bin damals auch mal über die "Stolperfalle" %d und %u
gefallen, da ich %u noch gar nicht kannte ;)

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K
spalte Haare so lange Du willst, aber verrate mir doch bitte endlich,
was statt %d im printf stehen soll, wenn das Argument eine unsigned int
ist,...

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@flyingwolf:

Hat er bereits!

>[..]Hier sollte wohl "%u" stehen.

Autor: flyingwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wer lesen kann ist klar im Vorteil. Damit ist das Thema nun erschöpfend
geklärt, aber keine Sorge. Schon bald werde ich jede Menge neuer
Programmierfehler fabriziert haben. mit denen ich mich hilfesuchend an
Euch wenden kann ;-)
fw

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.