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


von flyingwolf (Gast)


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;
  }

von OldBug (Gast)


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

von flyingwolf (Gast)


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.

von OldBug (Gast)


Lesenswert?

Hm, wird 'stelle' möglicherweise irgendwo in einem Interrupt
modifiziert?

von flyingwolf (Gast)


Lesenswert?

nein.

von A.K. (Gast)


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.

von Rufus T. Firefly (Gast)


Lesenswert?

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

von A.K. (Gast)


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.

von OldBug (Gast)


Angehängte Dateien:

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

von flyingwolf (Gast)


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

von OldBug (Gast)


Angehängte Dateien:

Lesenswert?

>Habe ich auch probiert, geht nicht, wird trotzdem negativ

Das liegt aber nicht am Programm, siehe Anhang!

von A.K. (Gast)


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.

von flyingwolf (Gast)


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?

von OldBug (Gast)


Angehängte Dateien:

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.

von OldBug (Gast)


Angehängte Dateien:

Lesenswert?

...hier der Output auf dem PC...

von tex (Gast)


Lesenswert?

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

von OldBug (Gast)


Lesenswert?

Hat da jemand Identitätsprobleme?

  :-)

von flyingwolf (Gast)


Lesenswert?

vermutlich mehr ein Kompetenzproblem.

von OldBug (Gast)


Lesenswert?

Wie meinen?

von tex (Gast)


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.

von A.K. (Gast)


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.

von flyingwolf (Gast)


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.

von peter dannegger (Gast)


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

von A.K. (Gast)


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.

von A.K. (Gast)


Lesenswert?

"Was gibt
  printf("%d", 4000);"

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

von OldBug (Gast)


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

von A.K. (Gast)


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.

von flyingwolf (Gast)


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.

von OldBug (Gast)


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

von A.K. (Gast)


Lesenswert?

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

von OldBug (Gast)


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

von flyingwolf (Gast)


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

von OldBug (Gast)


Lesenswert?

@flyingwolf:

Hat er bereits!

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

von flyingwolf (Gast)


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

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.