Hallo, ich habe ein kleines Problem: Ich addiere zwei 16bit Werte. Wie erkenne ich in C ob der Wert >65535 ist, also die Variable überläuft ? Folgendes funktioniert ja nicht: if ((a+b)>65535) da der uC ja nur mit 16bit Variablen rechnet und das Ergebnis somit nie wahr ist. Es geht um eine Regelschleife für eine Spannungsregelung. Wenn die Eingangsspannung aber unter der Ausgangsspannung liegt, regelt der Controller immer weiter hoch bis der Wert überläuft. Er soll dann einfach bei 65535 stehen bleiben. PS: Es ist ein M16C Controller.
Schau mal in der Beschreibung zum M16c nach: Flag register (FLG) Bit 5: Overflow flag (O flag) This flag is set to 1 when an arithmetic operation resulted in overflow; otherwise, cleared to 0. Das sollte dir bei deinem Problem helfen.... Gruß Peter
Wenn man zwei positive Zahlen addiert und das Ergebnis ist kleiner als die beiden Zahlen (jeweils einzeln) oder zwei negative Zahlen addiert und das Ergebnis ist größer als die beiden Zahlen, dann hat ein Überlauf stattgefunden. Addiert man eine positive und eine negative Zahl, dann kann kein Überlauf stattfinden. Also ungefähr so: summe = a + b; if ((a > 0) && (b > 0) && ((summe < a) || (summe < b))) { Überlauf } if ((a < 0) && (b < 0) && ((summe > a) || (summe > b))) { Überlauf } Markus
"...da der uC ja nur mit 16bit Variablen rechnet..." Na und ! Das interressiert doch keinen. long ist immer 32 bit und da kannst Du den Test bequem manchen oder komplett in long rechnen. Allerdings ist der Weg von Hans falsch, er macht erst die Addition und wandelt danach auf 32 Bit, da ist aber der Überlauf schon abgeschnitten. Oder in float, wenns nur für ne lahme Ausgabe auf ein Display ist. Dein Taschenrechner rechnet intern sogar nur 4-Bittig, genau wie ein Mensch nicht nur bis 10 zählen kann obwohl er nur 10 Finger hat. Peter
@Peter Kasi Schön, es gibt ein Overflow Flag, aber wie komme ich in C dran ? @peter dannegger Dann werde ich wohl long verwenden. Float braucht mir zuviel CPU Leistung.
Laut Peter sollte der Compiler die Bezeichnung FLG des Registers kennen (sonst musst du wohl oder übel ins Datenblatt schauen und die Adresse raussuchen), ergo ganz einfach wahrscheinlich if( FLG & 0x20) --> überlauf. Falls das Symbol FLG nicht bekannt ist, müsste so etwas wie if( *(U16*)0xRegisterAdresse & 0x20) funktionieren.
Habe gerade mal geschaut und bin mir sicher, daß dein Compiler sogar if(FLG_5) können müsste.
Überlaufflag: Alles in der Hoffnung, daß der Compiler nicht noch schnell einen anderen (Asm)-Befehl zwischenreinschiebt, der das OvlFlag nicht wieder manipuliert... -> unsaubere Lösung. Ich schließe mich der Meinung "in long rechnen" an und gut is. ----, (QuadDash).
1. Der Compiler kennt FLG nicht, (sonst wäre es ja auch kein Problem gewesen und ich hätte die Frage garnicht erst gestellt.) 2. Flag ist ein CPU Register und hat somit keine Adresse. Mann kann dieses Register in C also nicht auslesen...
Auch auf die Gefahr hin mich jetzt zu blamieren, aber kannst du nicht mit Hilfe von Assembler-Inlines an das Flagregister rankommen ? Thorsten
@peter dannegger: Habe grade keinen K&R zur Hand, allerdings habe ich noch in Erinnerung das die alle Operanden eines Ausdrucks auf den "groessten" in dem Ausdruck vorkommenden Datentyps konvertiert werden und danach auf den Ergebnistyp konvertiert werden. Ggf. sollte bei 16 Bit Compiler die Konstante auf long gecastet werden oder eine long Konstante (65535L) benutzt werden. Habe lange nicht mehr auf <32 Bit Systemen programmiert :-() Vieleicht kann jemand das Beispiel unten durch einen vernueftigen 16Bit Compiler jagen und den Assembler Output hier posten Beispiel: router:~# cat t.c main() { unsigned short a,b; long c; a=40000; b=40000; c=a+b; printf("res: %ld\n",c); if (((long)(a+b)) > 65535) { puts("true"); } } router:~# gcc -S t.c router:~# cat t.s .file "t.c" .version "01.01" gcc2_compiled.: .section .rodata .LC0: .string "res: %ld\n" .LC1: .string "true" .text .align 4 .globl main .type main,@function main: pushl %ebp movl %esp,%ebp subl $24,%esp movw $40000,-2(%ebp) movw $40000,-4(%ebp) movzwl -2(%ebp),%eax movzwl -4(%ebp),%edx leal (%edx,%eax),%ecx movl %ecx,-8(%ebp) addl $-8,%esp movl -8(%ebp),%eax pushl %eax pushl $.LC0 call printf addl $16,%esp movzwl -2(%ebp),%eax movzwl -4(%ebp),%edx addl %edx,%eax cmpl $65535,%eax jle .L3 addl $-12,%esp pushl $.LC1 call puts addl $16,%esp .L3: .L2: leave ret .Lfe1: .size main,.Lfe1-main .ident "GCC: (GNU) 2.95.4 20011002 (Debian prerelease)" router:~# ./t res: 80000 true router:~#
@hans "alle Operanden eines Ausdrucks auf den "groessten" in dem Ausdruck vorkommenden Datentyps konvertiert werden und danach auf den Ergebnistyp konvertiert werden." genau so isses ! Deshalb muß man zuerst a oder b nach long casten und *danach" addieren: if ((( (long)a +b)) > 65535) { Wenn Dein Compiler aber eine int Addition in long ausführen sollte, so ist das falsch. Peter
@ Peter Dannegger: Allerdings besteht der Ausdruck nicht nur aus "a+b" sondern aus "a+b > 65535". Auch c > d ist ein Ausdruck und deshalb besteht der Ausdruck aus den Operanden "a","b" und "65535" bzw. aus "(( (long)a +b))" und 65535. Man muesste ueberpruefen ob es spezielle Regeln fuer Teilausdruecke gibt, IMHO kann ich mir sowas schlecht vorstellen. Aber egal, ich werde am Wochenende mal nachsehen, allerdings halte ich den gcc nicht fuer den schlechtesten Compiler und halte es fuer unwahrscheinlich das deratiges falsch implementiert worden ist. Leider habe ich auch keine weiteren Compiler an der Hand mit denen ich das testen kann. Ggf. hast du ja andere zur Hand von denen man sich den Assembler Output ansehen kann, Ergebnisse bitte hier posten, denn langsam werde ich neugierig.
In C gibt es keine Ausdrücke und Teilausdrücke. Ein Ausdruck ist grob gesagt immer das, was man nicht weiter unterteilen kann. Du kannst eine beliebige Anzahl an Ausdrücken in eine Zeile schreiben, an der Art, wie sie ausgeführt werden, ändert das nichts (wäre sonst ja schlimm). Wenn ich exakt Deine Vergleichszeile in den AVR-GCC eingebe: char test( unsigned short a, unsigned short b ){ if (((long)(a+b)) > 65535) return 0; return 1; } kriege ich das hier: MAIN.C: In function `test': MAIN.C:4: warning: comparison is always false due to limited range of data type Und das Codelisting sieht dann so aus: 0000005c <test>: char test( unsigned short a, unsigned short b ){ if (((long)(a+b)) > 65535) return 0; return 1; } 5c: 81 e0 ldi r24, 0x01 ; 1 5e: 90 e0 ldi r25, 0x00 ; 0 60: 08 95 ret Er hat also den Vergleich komplett wegoptimiert. Und damit hat er recht. Peter
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.