Forum: Mikrocontroller und Digitale Elektronik Überlauf in C erkennen


von Benedikt (Gast)


Lesenswert?

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.

von hans (Gast)


Lesenswert?

if (((long)(a+b))>65535)

sollte gehen.

von Peter Kasi (Gast)


Lesenswert?

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

von Markus (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Benedikt (Gast)


Lesenswert?

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

von chrissy (Gast)


Lesenswert?

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.

von chrissy (Gast)


Lesenswert?

Habe gerade mal geschaut und bin mir sicher, daß dein Compiler sogar
if(FLG_5) können müsste.

von ---- (Gast)


Lesenswert?

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

von Benedikt (Gast)


Lesenswert?

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

von Thorsten (Gast)


Lesenswert?

Auch auf die Gefahr hin mich jetzt zu blamieren, aber kannst du nicht
mit Hilfe von Assembler-Inlines an das Flagregister rankommen ?

Thorsten

von hans (Gast)


Lesenswert?

@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:~#

von Peter D. (peda)


Lesenswert?

@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

von hans (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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
Noch kein Account? Hier anmelden.