Forum: Compiler & IDEs Überlauf von character variable abfangen


von Simo (Gast)


Lesenswert?

Hallo,

und zwar stehe ich vor einem Problem in der Programmiersprache C, bis 
jetzt habe ich es noch nicht geschaft das Problem zu lösen, vieleicht 
schaft es ja ein C-Spezialist unter euch....
Folgendes Problem:

Es soll ein unsigned character und ein signed character addiert werden, 
dabei sollen wie die Namen schon sagen, dem unsigned der volle 
Wertebereich zur Verfügung stehen und dem unsigned von -127 bis 128. 
Jedoch sollen die Überläufe abgefangen werden d.h es darf nie kleiner 0 
und nie größer 255 werden.

Ich habe schon begonnen das Problem zu lösen und schaffe es auch das der 
Unterlauf abgefangen wird also die Variable wird nie kleiner 0, aber der 
Überlauf über 255 klappt nicht....vieleicht hat einer von euch eine 
bessere Idee.

hier mal Stück C-Code:
1
  if  ( (summensignal[2] < summensignal_offset[2]))
2
     stick_pitch =0;
3
  else 
4
     tick_pitch = summensignal[2] - summensignal_offset[2];
5
  
6
   motor1 = stick_pitch + stick_nick;

Dabei ist stick_pitch und motor1 unsigned, sowie stick_nick ist signed.

Hoffe ihr könnt etas mit der Frage anfangen und danke schon mal!!

von J.-u. G. (juwe)


Lesenswert?

Berechne das Ergebnis doch zunächst mal als int (z.B. mittels temporärer 
Hilfsvariable). Dann prüfst Du auf den geforderten Wertebereich und 
machst anschließend die entsprechende Zuweisung für motor1.

von (prx) A. K. (prx)


Lesenswert?

Sehe grad das Problem nicht. Gerechnet wird
   unsigned_char + signed_char
formal sowieso in int, also mit grössere Breite. Das ausnutzen und 
anschliessend auf 0..255 clippen ist nicht weiter schwierig.

von frame (Gast)


Lesenswert?

> Jedoch sollen die Überläufe abgefangen werden d.h es darf nie
> kleiner 0 und nie größer 255 werden.

Das geht nicht.
C ist explizit so definiert, daß das Ergebnis in deinem Fall überrollt.
Wenn du in deiner Berechnung Überläufe abfangen willst, mußt du als 
temporären Zwischenwert einen entsprechend größeren Datentyp benutzen.

Überhaupt verstehe ich den Hintergrund nicht ganz.
'signed' und 'unsigned' sind quasi zwei verschiedene Interpretationen 
ein und derselben Speicherzelle. Das ist das gleiche wie Äpfel und 
Birnen addieren...

von J.-u. G. (juwe)


Lesenswert?

frame schrieb:
> Das ist das gleiche wie Äpfel und
> Birnen addieren...

Du meinst, bei der Rechnung x = 100 + (-50) werden Apfel und Birne 
adddiert?

von frame (Gast)


Lesenswert?

Ja, Mischobst.

von (prx) A. K. (prx)


Lesenswert?

frame schrieb:
> C ist explizit so definiert, daß das Ergebnis in deinem Fall überrollt.

Nein. Zumindest nicht solange sizeof(char) < sizeof(int), da das 
Zwischenergebnis vom Typ int ist. Erst durch eine anschliessende 
Truncation zu unsigned char geschieht dies.

von Simo (Gast)


Lesenswert?

wieso sollten das Unterschiede sein? nur die Interpretation des 
Zahlenkreis ist anders z.b 27 in unsigned ist das gleich in signed aus 
Bitebene. Nur negative Zahlen sind etwas anders. Ich mache mal ein 
kleines Bsp dazu vieleicht wird es dann klarer wo es hin soll...

Mit positven Zahlen:
200 + 40 = 40

200 +  60 = 255

Mit negativen Zahlen:
100 + (-60) = 40

100 + (-120) = 0

Hintergrund ist: ein Motor soll ein Stellsignal in einem postiven 
Bereich von 0-255 von einer Fernsteuerung bekommen, das Signal wird aus 
2 Hebeln der Steurung gemischt eben aus Pitch und aus Nick das auch 
negativ werden kann.....

Danke den Tipp mit dem größen Zahlenbereich werde ich mal testen...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?


von Norbert (Gast)


Lesenswert?

Simo schrieb:
> Ich mache mal ein
> kleines Bsp dazu vieleicht wird es dann klarer wo es hin soll...
>
> Mit positven Zahlen:
> 200 + 40 = 40
>
> 200 +  60 = 255
>
> Mit negativen Zahlen:
> 100 + (-60) = 40
>
> 100 + (-120) = 0

Bist Du sicher das dies auf allen Hardwareplatformen der Fall ist (zB 
auf  Mainframes* (ja, dafür gibt's auch C compiler))?

* IBM, UNISYS, SPERRY UNIVAC

von (prx) A. K. (prx)


Lesenswert?

Norbert schrieb:
> UNISYS, SPERRY UNIVAC

Gibts die noch?

von (prx) A. K. (prx)


Lesenswert?

Norbert schrieb:
> Bist Du sicher das dies auf allen Hardwareplatformen der Fall ist

Hardwareseitige Saturierung ist eher für DSPs und MMX/SSE/... 
Befehlssätze typisch - ein Bereich, in dem sich Mainframes eher selten 
bewegen.

Wenn er das softwareseitig abfängt: Wo sollten Probleme auftauchen? Ok, 
signed char definiert nur -127..+127, aber das ist die einzige 
Einschränkung.

von Norbert (Gast)


Lesenswert?

Ja klar, na gut teilweise. Check mal bei der Lusthansa ein 
(Ticket-Gateways Unisys)

Dabei hab ich mich noch nicht einmal getraut meinen guten alten 
Perkin-Elmer zu nennen (erste 'C' Gehversuche)

von Rolf Magnus (Gast)


Lesenswert?

J.-u. G. schrieb:
> frame schrieb:
>> Das ist das gleiche wie Äpfel und
>> Birnen addieren...
>
> Du meinst, bei der Rechnung x = 100 + (-50) werden Apfel und Birne
> adddiert?

Nein. Die Werte sind beide vorzeichenbehaftet. Nur weil ein Wert positiv 
ist, macht ihn das ja nicht automatisch vorzeichenlos.

von Simo (Gast)


Lesenswert?

hervoragend :-) habe das Problem jetzt mit einer Tempvariablen mit 
größerem Wertebereich gelöst.
hier für alle die es interresiert....
1
temp_vari = (int16_t) (stick_pitch + stick_nick);
2
3
if (temp_vari > 255) temp_vari = 255;
4
if (temp_vari < 0) temp_vari = 0;
5
motor1 = temp_vari;

nachmals danke!!

von (prx) A. K. (prx)


Lesenswert?

Wozu der Cast?

von Karl H. (kbuchegg)


Lesenswert?

Simo schrieb:

> temp_vari = (int16_t) (stick_pitch + stick_nick);

Den Cast kannst du dir sparen. Er bewirkt sowieso nichts.

von Karl H. (kbuchegg)


Lesenswert?

Nochmal:
In C gibt es eine Regel. Die besagt, dass Berechnungen grundsätzlich 
mindestens im kleinsten Datentyp 'int' ausgeführt werden. Es spielt 
schlicht und ergreifend keine Rolle ob du einen unsigned char mit einem 
signed char addierst. Denn als erstes werden beide Additionspartner 
sowieso zu einem int 'befördert', ehe dann die Addition ausgeführt wird.

Genau darum funktioniert deine Version. Denn das Ergebnis von

    stick_pitch + stick_nick

ist bereits ein int. Auch ohne Cast.

von (prx) A. K. (prx)


Lesenswert?

PS: Wenn du mit dem Cast sicherstellen wolltest, dass auch ein schräg 
konfigurierter µC-Compiler das in 16 Bits rechnet - manche sind so 
einstellbar, dass standardwidrig aber effizienter keine Erweiterung 
auf int stattfindet - dann funktioniert das so nicht. Dann würde der es 
in 8 Bit Rechnung addieren und erst das falsche Ergebnis zu 16 Bits 
machen.

Dann geht das so:
   temp_vari = (int16_t)stick_pitch + (int16_t)stick_nick;

von Simo (Gast)


Lesenswert?

danke, das wusste ich garnicht, dass ehh alles in integer gerechnet 
wird. Ist das nicht Ressourcenverschwendung?

Habe bis jetzt immer in Assembler gerechnet da war immer("meistens" :-) 
) klar was an welcher Stelle passiert....

von (prx) A. K. (prx)


Lesenswert?

Simo schrieb:
> danke, das wusste ich garnicht, dass ehh alles in integer gerechnet
> wird. Ist das nicht Ressourcenverschwendung?

Das ist nun einmal so definiert - und anfangs war es auch keine 
Resourcenverschwendung, weils die damals in C massgebliche PDP-11 intern 
sowieso in genau dieser Weise gerechnet hat.

von Karl H. (kbuchegg)


Lesenswert?

Simo schrieb:
> danke, das wusste ich garnicht, dass ehh alles in integer gerechnet
> wird. Ist das nicht Ressourcenverschwendung?

Jain.
Es steht ja dem Optimizer frei, das Ganze wieder umzustossen, wenn er 
nachweisen kann, dass das Ergebnis dasselbe ist, wenn er anstelle einer 
16-Bit Berechnung auf 8 Bit runterkürzt.

>
> Habe bis jetzt immer in Assembler gerechnet da war immer("meistens" :-)
> ) klar was an welcher Stelle passiert....

AUch hier ist es klar, wenn man sein C gelernt hat.
Und genau aus dem Grund schreib ich mitlerweile unter jeden 3.ten Thread 
drunter: Kauf dir ein C-Buch! Da stehen solche Sachen (und noch 100-erte 
andere) nämlich drinnen.

von Simo (Gast)


Lesenswert?

also ich habe ein C-Buch, dass ist vieleicht nicht das Dickste und 
Beste, aber die Grundlagen stehen drin, allerdings besonders auf 
Berechnungen solcher Art werden nicht eingegangen!!! Welcher Buch kannst 
du denn empfehlen, in dem auch solch Dinge stehen @ Karl Heinz 
Buchegger?

von Karl H. (kbuchegg)


Lesenswert?

Simo schrieb:
> also ich habe ein C-Buch, dass ist vieleicht nicht das Dickste und
> Beste, aber die Grundlagen stehen drin, allerdings besonders auf
> Berechnungen solcher Art werden nicht eingegangen!!! Welcher Buch kannst
> du denn empfehlen, in dem auch solch Dinge stehen @ Karl Heinz
> Buchegger?

Wie immer den Klassiker

Kernighan & Ritchie
Programmieren in C


Wenn geht, die englische Original-Version. (keine Angst. das ist eine 
technische Doku und kein literarisches Meisterwerk. Mit Schulenglisch 
kommt man gut zurecht)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:

> Wenn er das softwareseitig abfängt: Wo sollten Probleme auftauchen? Ok,
> signed char definiert nur -127..+127, aber das ist die einzige
> Einschränkung.

Wenn es C ist stlopert man gerne darüber daß signed-Overflow undefined 
ist:
1
int abs1 (int x)
2
{
3
    if (x < 0)
4
        x = -x;
5
        
6
    // Überlauf wegen -INT_MIN = INT_MIN = 0x800... ?
7
    return x < 0 ? INT_MAX : x;
8
    // Ebenso aussichtslos: (x & (1 << (8 * sizeof (int) -1)))
9
}
10
11
int abs2 (int x2)
12
{
13
    unsigned x = x2;
14
    
15
    if ((int) x < 0)
16
        x = -x;
17
        
18
    // Überlauf wegen -INT_MIN = INT_MIN = 0x800... ?
19
    return (int) x < 0 ? INT_MAX : x;
20
}
 
Einfach mal mit einem nicht allzu alten GCC compilieren.

Neben diesem händischen Saturieren gibt es auch die Embedded-C 
Erweiterung ISO/IEC TR 18037, die saturierte Arithnmetik implementiert. 
Einige Compiler wie avr-gcc unterstützen diese C-Erweiterung.

von (prx) A. K. (prx)


Lesenswert?

Wenn es wie hier um die Addition zweier chars geht, dann kann man das 
Thema Überlauf grosszügiger sehen. ;-)

von Karlo (Gast)


Lesenswert?

Hallo,

ich dachte immer, Berechnungen werden im größeren der beiden Datentypen 
durchgeführt, die an der Rechnung beteiligt sind?

Vor einiger Zeit hab ich mal einen uint16 mit einem Konstanten Wert von 
3000 multipliziert. Erst nachdem ich hier im Forum nachgefragt und einen 
Faktor nach uint32 gecastet habe hats geklappt. Auf einem AVR entspricht 
INT aber einem 32Bit Datentyp, also hätte doch der Cast nicht notwendig 
sein dürfen?

Kann mal schaun ob ich den Thread noch finde...

von (prx) A. K. (prx)


Lesenswert?

Karlo schrieb:
> ich dachte immer, Berechnungen werden im größeren der beiden Datentypen
> durchgeführt, die an der Rechnung beteiligt sind?

Auch. Aber mindestens in "int". Steht in jedem C Handbuch.

> Auf einem AVR entspricht INT aber einem 32Bit Datentyp

Was immer ein INT ist - ein int hat bei AVRs 16 Bits.

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.