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; }
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 ;-)
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.
Hm, wird 'stelle' möglicherweise irgendwo in einem Interrupt modifiziert?
> 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.
Wie kann temp_Controller_ID vom Typ "unsigned int" sein und kleiner werden als 0?
> 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.
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
@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
>Habe ich auch probiert, geht nicht, wird trotzdem negativ
Das liegt aber nicht am Programm, siehe Anhang!
"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.
@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?
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.
ich liefere es morgen früh zusammen mit dem EEProm Inhalt. Erst mal ganz herzlichen Dank für die Hilfe
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.
"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.
@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.
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
"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.
"Was gibt printf("%d", 4000);" Sorry, da fehlt eine 0. Sollte printf("%d", 40000); heisse.
@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...
@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.
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.
@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
@OldBug: ok, Frieden :-), ich werde versuchen die Haare nicht noch weiter zu spalten.
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 ;)
@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,...
@flyingwolf:
Hat er bereits!
>[..]Hier sollte wohl "%u" stehen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.