Kleine Positiv/Negativ Rechnung in C

Gast #1050932
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.
#1050937
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.
Moderator (Firma: Titel) Persönliche Seite #1050938
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.
#1050947
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
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"
Gast #1050959
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.
#1050974
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
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
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
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
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

Eine eigene Frage? Dieser Thread ist über 17 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.