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.