mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Überlauf in C erkennen


Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
if (((long)(a+b))>65535)

sollte gehen.

Autor: Peter Kasi (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: chrissy (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: chrissy (Gast)
Datum:

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

Autor: ---- (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Thorsten (Gast)
Datum:

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

Thorsten

Autor: hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:~#

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.