Kleine Positiv/Negativ Rechnung in C

Gast #1050932
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.
#1050947
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.
#1050956
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"
#1050974
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.
#1050978
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.
#1051000
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.
#1051032
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.
#1051040
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.)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren