Forum: Mikrocontroller und Digitale Elektronik Kleine Positiv/Negativ Rechnung in C


von Sebastian (Gast)


Lesenswert?

Irgendwie habe ich gerade einen Blackout betreffend Positiv/Negativ 
Rechnung im C. Ich hoffe ihr könnt mir da auf die Sprünge helfen.

Also ich habe einfach zwei Variablen, die ich voneinander abziehe und 
dann noch durch 2 teile.
Mit dem erhaltenen Wert (negativ oder positiv - beides möglich) 
erweitere ich dann eine dritte Variable.

Also etwa so:
1
Int z,c;
2
Unsigned Int a,b;
3
4
z = (a - b) >> 1;
5
6
c = z;

Leider funktioniert das ganze nicht und wenn ich debugge erhalte ich 
folgende Werte. a und b stimmen, z nicht und demzufolge c auch nicht.

z = (22'892 - 22'946) >> 2 = 32'741      (eigentlich ja -27)

Ich dachte immer, dass negative Zahlen in einer integer variable mit dem 
vordersten Bit, also Bit 15 angezeigt werde und aber die 27 ganz normal 
mit Bit 0-4.

von Benedikt K. (benedikt)


Lesenswert?

Verwende anstelle des >>1 besser ein /2. Die Implementierung von >>1 für 
negative Zahlen ist nicht festgelegt. Es kann das richtige rauskommen, 
muss aber nicht.

von Stefan KM (Gast)


Lesenswert?

Wieso schiebst du unten 2 mal und oben nur einmal?

von (prx) A. K. (prx)


Lesenswert?

Ist gleich doppelt falsch:
22892u - 22946u = 0xFFCAu.
0xFFCAu >> 2 = 0x3FF2u.

Ein negatives Ergebnis käme beim Shift nur raus, wenn das Ergebnis der 
Subtraktion ein Vorzeichen hätten, was angesichts 2x "unsigned int" aber 
nicht der Fall ist.

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


Lesenswert?

Haben wir hier vielleicht einen Prozessor, bei dem INT = 16Bit ist?
Versuch das doch mal mit long/unsigned long oder int32_t/uint32_t.

Oder schreibs so:
1
z = ((int)a - (int)b) >> 1;
Denn bei deinem Original wird die Rechnung selber unsigned gemacht.
Erst das Ergebnis wird signed interpretiert, und das ist wieder positiv.

von (prx) A. K. (prx)


Lesenswert?

Benedikt K. wrote:

> Verwende anstelle des >>1 besser ein /2. Die Implementierung von >>1 für
> negative Zahlen ist nicht festgelegt.

Trifft hier nicht zu, da eine vorzeichenlose Zahl nicht negativ sein 
kann. Und ich meine auch, dass seit ANSI C das Verhalten definiert ist, 
nur K&R C das bei vorzeichenbehafteten Datentypen im Unklaren lies.

von Benedikt K. (benedikt)


Lesenswert?

A. K. wrote:
> Benedikt K. wrote:
>
>> Verwende anstelle des >>1 besser ein /2. Die Implementierung von >>1 für
>> negative Zahlen ist nicht festgelegt.
>
> Trifft hier nicht zu, da eine vorzeichenlose Zahl nicht negativ sein
> kann.

Wenn man beide voneinander abzieht, dann schon. Er hat halt auch noch 
den cast auf signed int vergessen.
Ansonsten passt die Rechnung:
22892 - 22946 = 65482 (wenn als unsigned gerechnet) und 65482/2=32741
22892 - 22946 = -54 (wenn als signed gerechnet) und -54/2=-27

> Und ich meine auch, dass seit ANSI C das Verhalten definiert ist,
> nur K&R C das bei vorzeichenbehafteten Datentypen im Unklaren lies.

Keine Ahnung, zumindest wurde es hier im Forum schon mehrmals erwähnt:
Beitrag "Re: Frage zu C-Code"

von Sebastian (Gast)


Lesenswert?

also bei mir ist es so:

a,b sind unsigned integer mit 16-Bit

z ist signed integer mit 16-Bit

c ist signed integer mit 32-Bit


also mit
z = (a - b) / 2;
c = z;

funktioniert es auch nicht.

von Benedikt K. (benedikt)


Lesenswert?

Sebastian wrote:
> also mit
> z = (a - b) / 2;
> c = z;
>
> funktioniert es auch nicht.

Versuchs mal so:
z = ((signed int)a - b) / 2;

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


Lesenswert?

Sebastian wrote:
> also mit
> z = (a - b) / 2;
> c = z;
>
> funktioniert es auch nicht.

Ich zitiere mich selber:
> Denn bei deinem Original wird die Rechnung selber unsigned gemacht.
> Erst das Ergebnis wird signed interpretiert, und das ist wieder positiv.

von (prx) A. K. (prx)


Lesenswert?

Benedikt K. wrote:

> Wenn man beide voneinander abzieht, dann schon.

Das Ergebnis zweier "unsigned int" ist per Definition nicht negativ, 
folglich ist der Shift-Operator auch schon in K&R C klar definiert.

> 22892 - 22946 = 65482 (wenn als unsigned gerechnet) und 65482/2=32741

Ok, einfach geschoben ja, das hatte nicht gleich gesehen.

von Benedikt K. (benedikt)


Lesenswert?

A. K. wrote:
> Benedikt K. wrote:
>
>> Wenn man beide voneinander abzieht, dann schon.
>
> Das Ergebnis zweier "unsigned int" ist per Definition nicht negativ.

Daher schrieb ich ja:
> Er hat halt auch noch den cast auf signed int vergessen.

Denn das ist das was er möchte, mit dem unsigned int Ergebnis kann er 
wenig anfangen.

von Sebastian (Gast)


Lesenswert?

z = ((signed int)a - b) / 2;


So funktioniert es. Danke euch.

von (prx) A. K. (prx)


Lesenswert?

Das ist jetzt schon etwas erstaunlich. Denn
  ((signed int)a - b)
ist immer noch vorzeichenlos. Erst
  ((signed int)(a - b))
ist es nicht mehr.

von Benedikt K. (benedikt)


Lesenswert?

Das ist mir auch gerade aufgefallen, aber ich konnte leider nirgends 
eine Erklärung dazu finden (weder im K&R noch sonst wo).

Wie funktioniert ein cast denn eigentlich? Wirkt der auf die ganze 
Operation oder nur auf eine Variable?
Wenn letzeres dann wäre es ja signed int - unsigned int und das müsste 
dann zu unsigend int - unsigend int werden.
Bisher habe ich immer den cast innerhalb der Klammer gemacht, denn 
ansonsten würde es ja als unsigned int - unsigned int gerechnet werden. 
In diesem Fall wäre es daher nicht schlimm, wenn der cast Außerhalb der 
Klammer ist, denn -54 und 65590 sind quasi identisch, nur 
unterschiedlich interpretiert.
Aber spätestens bei (a*b) macht es einen gewaltigen Unterschied, ob die 
Operation oder nur das Ergebnis als signed ausgeführt wird.

von (prx) A. K. (prx)


Lesenswert?

Benedikt K. wrote:

> Das ist mir auch gerade aufgefallen, aber ich konnte leider nirgends
> eine Erklärung dazu finden (weder im K&R noch sonst wo).

Hab den Standard grad nicht rumliegen, aber bei "unsigned" <=> "signed" 
gleicher Bitbreite und sizeof(int) aufwärts gewinnt "unsigned".

Bei sizeof(unsigned) < sizeof(signed) hingegen gewinnt "signed". Aber 
erst ab ANSI C89, bei K&R was dies schwach oder garnicht definiert und 
oft als "unsigned gewinnt immer" implementiert.

> Wie funktioniert ein cast denn eigentlich? Wirkt der auf die ganze
> Operation oder nur auf eine Variable?

Der cast ist ein normaler unary operator wie ~0 oder !1. Folglich ist
   ((int)a - b)
identisch mit
   (((int)a) - b)
also von den Typen her
   signed - unsigned
und das ist unsigned.

> Aber spätestens bei (a*b) macht es einen gewaltigen Unterschied, ob die
> Operation oder nur das Ergebnis als signed ausgeführt wird.

Korrekt. Mitdenken ist da Pflicht. Weil das Ergebnis einer 
Multiplikation mit Überlauf nur bei vorzeichenloser Rechnung definiert 
ist (Modulo), vorzeichenbehaftet jedoch nicht. Rein technisch kommt in 
der bei C einzig übrig bleibenden unteren Produkthälfte das gleiche 
Bitmuster raus, nur die obere Produkthälfte unterscheidet sich.

von Benedikt K. (benedikt)


Lesenswert?

A. K. wrote:

> Hab den Standard grad nicht rumliegen, aber bei "unsigned" <=> "signed"
> gleicher Bitbreite und sizeof(int) aufwärts gewinnt "unsigned".
>
 ist ein normaler unary operator wie ~0 oder !1. Folglich ist
>    ((int)a - b)
> identisch mit
>    (((int)a) - b)
> als von den Typen her
>    signed - unsigned
> und das ist unsigned.

Ok, dann passt meine Vorstellung. Aber wieso funktioniert jetzt aber 
dieses hier?
z = ((signed int)a - b) / 2;

Streng genommen dürfte es nicht funktionieren, denn es ist ja signed int 
- unsigned int was zu unsigned int wird, was durch 2 geteilt wird. 
Interessanterweise funktionier es aber (was mir zumindest auch meine 
Erfahrung sagt, denn bisher habe ich das immer so gemacht, und mir nicht 
all zu viele Gedanken darüber gemacht. Streng genommen ist es aber 
falsch und dürfte nicht funktionieren.)

von (prx) A. K. (prx)


Lesenswert?

Wie schon erwähnt dürfte auch das nicht funktionieren. Es wäre aber 
nicht der erste Tipp/Übernahmefehler in diesem Thread.

von Sebastian (Gast)


Lesenswert?

z = ((signed int)a - b) / 2;

funktioniert nicht.


z = (signed int)(a - b) / 2;

so funktioniert es.

von Benedikt K. (benedikt)


Lesenswert?

OK, dann passt ja alles.
Die automatischen Typenumwandlungen sind wirklich eine gemeine Sache. 
Meistens merkt man diese Fehler auch nicht sofort, außer man kommt 
zufällig bei den Tests in einen gewissen Wertebereich wo die Fehler 
auftreten.

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.