mikrocontroller.net

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


Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
Int z,c;
Unsigned Int a,b;

z = (a - b) >> 1;

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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan KM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieso schiebst du unten 2 mal und oben nur einmal?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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"

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

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

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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
z = ((signed int)a - b) / 2;


So funktioniert es. Danke euch.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: A. K. (prx)
Datum:

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

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
z = ((signed int)a - b) / 2;

funktioniert nicht.


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

so funktioniert es.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.