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
Intz,c;
2
UnsignedInta,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.
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.
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.
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.
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.
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"
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.
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.
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.
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.
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.
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.
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.)
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.