Forum: Mikrocontroller und Digitale Elektronik Überlauf macht Vorzeichenwechsel


von Bastelmaus (Gast)


Lesenswert?

Hallo,
ich hätte mal ein Problem beim C Programmieren:
1
unsigned long int a,b,c;
2
3
b = blablub 
4
c = blubbla
5
6
a = b + c; // Hier könnte ein Überlauf passieren
Ich addiere zwei signed long int. Wie kann ich verhindern, das bei 
Überlauf das Vorzeichen des Ergebnisses falsch ist?
Ich würde dann lieber das Ergebnis auf 0x7FFFFFFF bzw. 0x80000000 
begrenzt haben?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bastelmaus schrieb:
> Wie kann ich verhindern, das bei Überlauf das Vorzeichen
> des Ergebnisses falsch ist?
Bei einem unsigned long kann niemals das Vorzeichen falsch sein. Es 
kann bestenfalls das Ergebniss an sich falsch sein...

> Ich würde dann lieber das Ergebnis auf 0x7FFFFFFF bzw. 0x80000000
> begrenzt haben?
Warum?

Oder willst du einfach nur erkennen, dass du einen Überlauf hattest?

von Frank B. (foobar)


Lesenswert?

Hier gibt es ein paar Ideen dazu:

http://bytes.com/topic/c/answers/523303-how-test-overflow

(erstes Suchergebnis bei Google nach "c overflow test")

Wirklich schade, daß C da nichts besseres bietet, denn viele CPUs haben 
intern ja ein Flag, daß anzeigt, ob die vorherige Rechnenoperation 
übergelaufen ist. Mir fällt da auf Anhieb der gute alte 6502 ein:

http://www.6502.org/tutorials/vflag.html

von Michael G. (linuxgeek) Benutzerseite


Lesenswert?

Lothar Miller schrieb:
> Bastelmaus schrieb:
>> Wie kann ich verhindern, das bei Überlauf das Vorzeichen
>> des Ergebnisses falsch ist?
> Bei einem unsigned long kann niemals das Vorzeichen falsch sein. Es
> kann bestenfalls das Ergebniss an sich falsch sein...

Das Ergebnis ist immer richtig wenn man beruecksichtigt dass es sich 
hier um einen Restklassenring handelt.

von Frank B. (foobar)


Lesenswert?

Michael G. schrieb:
> Das Ergebnis ist immer richtig wenn man beruecksichtigt dass es sich
> hier um einen Restklassenring handelt.

Das mag meistens so sein, aber laut C Standard ist ein Integer Overflow 
nicht definiert und implementierungsabhängig. Ein mögliches Verhalten 
bei einem Integer Overflow wäre also auch, daß der Chip in Rauch aufgeht 
:-)

von Bastelmaus (Gast)


Lesenswert?

Bastelmaus schrieb:
> unsigned long int a,b,c;

oh Mist ich wollte natürlich schreiben :-(
1
signed long int a,b,c;
2
3
b = blablub 
4
c = blubbla
5
6
a = b + c; // Hier könnte ein Überlauf passieren
sorry tut mir ganz doll leid, ich war zu hastig.
Können wir nochmal ganz von vorne anfangen?

von Thomas E. (thomase)


Lesenswert?

Was ist denn jetzt das Problem?

Einen Überlauf bei zwei positiven Zahlen hast Du, wenn das Ergebnis(a) 
kleiner ist als der größere der beiden Operanden(b,c).

Mfg

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bastelmaus schrieb:
> Können wir nochmal ganz von vorne anfangen?
Dann kannst du abfragen, ob sich bei der Addition ein unerwartetes 
Ergebnis ergibt und den Wert anschliessend begrenzen. Etwa so:
1
  signed long int a,b,c;
2
3
  b = blablub 
4
  c = blubbla
5
6
  a = b + c;
7
  if (a<0 && b>0 && c>0) a=0x7fffffff;
8
  if (a>0 && b<0 && c<0) a=0x80000000;

von Karl H. (kbuchegg)


Lesenswert?

Du kannst auch einen Overflow detektieren, indem du vom maximal 
möglichen signed int Wert zuerst a abziehst. Dadurch erhältst du einen 
Wert, den b nicht überschreiten darf, weil es sonst zu einem Overflow 
kommt.

Hat ein bischen was von:
   Ich hab schon 80 Euro Schulden
   Wenn ich maximal in Summe 100 Euro Schulden machen darf,
   wieviele Schulden dürfen dann noch dazu kommen, damit mein
   100€ Budget nicht überschritten wird?
   Ich würde gerne noch 40€ Schulden machen, überlaufe ich damit
   meinen Rahmen oder nicht?

Wo das hinführt, wenn man Overflows erst im Nachhinein detektiert, sieht 
man ja aktuell ...

von Bastelmaus (Gast)


Lesenswert?

Lothar Miller schrieb:
1
> a = b + c;
2
> if (a<0 && b>0 && c>0) a=0x7fffffff;
3
> if (a>0 && b<0 && c<0) a=0x80000000;
Vielen Dank :-) ich hatte Tomaten auf den Augen und hab den Wald vor 
lauter Bäumen nicht gesehen.

Aber müsste man nicht auch berücksichtigen wenn a = 0 ist?

von Jens G. (jensig)


Lesenswert?

a = 0 ist doch kein Überlauf, sondern bedeutet nur, daß |+b|=|-c| (oder 
umgekehrt) war.

von Bastelmaus (Gast)


Lesenswert?

Jens G. schrieb:
> a = 0 ist doch kein Überlauf, sondern bedeutet nur, daß |+b|=|-c| (oder
> umgekehrt) war.

Ja eben nicht,
wenn z.B.
1
b = 0x70000000;
2
c = 0x90000000;
3
a = b + c; //Jetzt kommt meine Rakete vom Kurs ab :-(

von Andreas B. (Gast)


Lesenswert?

Bastelmaus schrieb:
> Ja eben nicht,
> wenn z.B.
1
b = 0x70000000;
2
c = 0x90000000;
3
a = b + c; //Jetzt kommt meine Rakete vom Kurs ab :-(

Angenommen 32 Bit vorzeichenbehaftete Variablen ist das doch exakt der 
Fall

Jens G. schrieb:
> |+b|=|-c|

Also wieso "eben nicht"?

von Jens G. (jensig)


Lesenswert?

Bastelmaus schrieb:
> Ja eben nicht,
> wenn z.B.

>b = 0x70000000;
>c = 0x90000000;
>a = b + c; //Jetzt kommt meine Rakete vom Kurs ab :-(

Wie soll da a=0 rauskommen? Ich verstehe die Spezialität mit a=0 nicht 
:-(

von Andreas B. (Gast)


Lesenswert?

Nix Spezialität:

0x70000000 =  1879048192
0x90000000 = -1879048192

Alles angenommen 32 Bit signed. Oder einfacher ohne Berücksichtigung 
signed/unsigned (die reine Addition braucht im 2er-Komplement darauf 
keine Rücksicht nehmen) 0x70000000 + 0x90000000 = 0x100000000. In 32 Bit 
passt das nicht mehr, die 1 fällt links raus (bzw. ins Carry-Flag) und 
die restlichen Bits sind ja alle 0.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> 0x70000000 =  1879048192
> 0x90000000 = -1879048192
> 0x70000000 + 0x90000000 = 0x100000000
Also gibt 1879048192 + -1879048192 = 0
Ein Übertrag ist vollkommen uninteressant, und muß nicht extra behandelt 
werden.

Kurz: wenn eine positive un eine negative Zahl addiert wird, kann (für 
das Ergebnis) kein Überlauf passieren. Und dann muß auch kein Überlauf 
behandelt werden.

von Bastelmaus (Gast)


Lesenswert?

Man muß noch einen Fall berücksichtigen:
0x80000000 + 0x80000000

von Bastelmaus (Gast)


Lesenswert?

Ich hätte noch eine Frage, die ganz ähnlich ist, deshalb wollte ich da 
erstmal keinen neuen Thread starten.
Ich habe:
1
signed long int a,b;
2
a = bla;
3
b = blub;
4
a += b; // wie den Überlauf/Vorzeichenwechsel hier abfangen? 
5
a -= b; // Neue Variable a_alt einführen?

von Haku (Gast)


Lesenswert?

Es ist aber in der Tat so:
Überlauf bei vorzeichenbehafteten Ganzzahlen ist undefiniertes 
Verhalten. Der Prozessor darf sich erhängen, der Compiler darf das 
Programm beenden und so weiter.

Siehe dazu:
https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

von Simon K. (simon) Benutzerseite


Lesenswert?

Der Compiler darf das Programm beenden? Ja ne is klar!

von Karl H. (kbuchegg)


Lesenswert?

Simon K. schrieb:
> Der Compiler darf das Programm beenden? Ja ne is klar!

Auf einer Z80 würde das gehen. Der hat eine HALT Instruktion. Danach 
macht die CPU nichts mehr :-)
Der Compiler könnte die für den Fall des Falles einbauen. Indirekt hat 
er damit das Programm beendet :-)

von Karl H. (kbuchegg)


Lesenswert?

Bastelmaus schrieb:
> Ich hätte noch eine Frage, die ganz ähnlich ist, deshalb wollte ich da
> erstmal keinen neuen Thread starten.

Und welchen der vielen Vorschläge von weiter oben hast du nicht 
verstanden?

von Haku (Gast)


Lesenswert?

Wenn der Compiler etwa einen Sprung ans Ende des Programmes plaziert 
oder einen Rücksprung ins Betriebssystem (exit, abort, return, trap, 
int, ...), dann ist das Programm für meine Verhältnisse dort zu Ende.

Oder möchtest du jetzt Haare spalten?

von Bastelmaus (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Bastelmaus schrieb:
>> Ich hätte noch eine Frage, die ganz ähnlich ist, deshalb wollte ich da
>> erstmal keinen neuen Thread starten.
>
> Und welchen der vielen Vorschläge von weiter oben hast du nicht
> verstanden?

Lieber Karl heinz Buchegger,  :-)
ich denke ich habe den Vorschlag von Lothar Miller schon verstanden,
und bin auch drauf gekommen, das man auch den Fall berücksichtigen 
sollte, das b und c = -2147483648 sind.
Dann ist das falsche Ergebnis nämlich 0  :-(
Dies kann mit der Abfrage if (a>=0 ... korrigiert werden.  :-)
1
  signed long int a,b,c;
2
  b = -2147483648;
3
  c = -2147483648;
4
5
  a = b + c;
6
  if (a<0 && b>0 && c>0) a=0x7FFFFFFF;
7
  if (a>=0 && b<0 && c<0) a=0x80000000;

Meine neue Frage bezieht sich darauf, wie man es am besten macht, wenn 
man nicht a = b + c berechnen will, sondern a += b;
Ist es dann schlau, vor der Addition eine Kopie a_alt von a zu machen?
1
  signed long int a,a_alt,b;
2
  a = bla;
3
  b = blub;
4
5
        a_alt = a; // a_alt ist eine Kopie von a
6
  a += b;
7
  if (a<0 && b>0 && a_alt>0) a=0x7FFFFFFF;
8
  if (a>=0 && b<0 && a_alt<0) a=0x80000000;
Oder gibt es da noch einen Zaubertrick?  ;-)

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.