Forum: Mikrocontroller und Digitale Elektronik signed / unsigned casten


von mr.chip (Gast)


Lesenswert?

Hallo

Ich habe folgendes Problem: Ich habe einen Vektor x, der um zwei 
Streckfaktoren a und b in x- und y-Richtung gestreckt werden soll. Zu 
diesem gestreckten Vektor wird noch ein zweiter Vektor y addiert. Der 
Vektor x sowie die Steckfaktoren a und b sind vom Typ int_8t, der Vektor 
y sowie das Endresultat sind vom Typ uint8_t. Aber ich kriege das allen 
ernstens nicht gebacken! Offenbar gibt es ein ziemlich mächtiges Problem 
mit dem Type-Casting. Aktueller Ansatz:
1
  int16_t tx = (uint16_t)dx * (uint16_t)pgm_read_byte(xvect + cnt);
2
  int16_t ty = (uint16_t)dy * (uint16_t)pgm_read_byte(yvect + cnt);
3
4
  
5
  
6
  tx = tx / (int16_t)64;
7
  ty = ty / (int16_t)64;
8
  
9
  uint8_t x = (uint8_t) (tx + ox);
10
  uint8_t y = (uint8_t) (ty + oy);

Wobei dx = a, dy = b. Der Vektor x wird per pgm_read_byte(...) aus dem 
Flash gelesen. Der Vektor y wird durch ox, oy repräsentiert.

Was läuft hier alles falsch, und wie mache ich es besser? :-)

Gruss

Michael

von yalu (Gast)


Lesenswert?

Du musst für jedem Zwischenschritt überlegen, ob der Wertebereich des
verwendeten Datentyps groß genug ist, um alle tatsächlich
vorgeommenden Ergebnisse darzustellen.

Die Produkte in den ersten beiden Zeilen könnten Kandidaten für
Wertebereichsüberschreitungen sein, ebenso die uint8_t-Casts in den
letzten beiden Zeilen.

Du könntest die Wertebereiche von dx, dy, ox, oy und der mit
pgm_read_byte gelesenen Parameter posten. Dann sieht hier sicher
einer, wo es hängt. Vielleicht hast du auch ein Zahlenbeispiel, für
das ein fehlerhaftens Ergebnis geliefert wird.

von mr.chip (Gast)


Lesenswert?

Naja...was passiert eigentlich genau, wenn ich einen int8_t in einen 
uint8_t caste? Wird da aus (beispielsweise) einem signed 120 immer noch 
ein unsigned 120? (Meine Simulationsergebnisse mit AVR Studio 
widersprechen dem eher, wobei ich noch nicht ganz verstanden habe, wo 
und wann Probleme auftreten.)

von yalu (Gast)


Lesenswert?

120 sollte man beliebig zwischen int8_t und uint8_t hin- und hercasten
können, da diese Zahl in beiden Datentypen darstellbar ist.
(uint8_t)-200 hingegen ergibt 56 und (int8_t)200 ergibt -56, weil die
Zahlenwerte vor dem Casten außerhalb des Wertebereichs des jeweiligen
Zieldatentyps liegen.

von Karl H. (kbuchegg)


Lesenswert?

mr.chip wrote:
> Naja...was passiert eigentlich genau, wenn ich einen int8_t in einen
> uint8_t caste? Wird da aus (beispielsweise) einem signed 120 immer noch
> ein unsigned 120? (Meine Simulationsergebnisse mit AVR Studio
> widersprechen dem eher, wobei ich noch nicht ganz verstanden habe, wo
> und wann Probleme auftreten.)

ZUnächst mal musst du berücksichtigen, dass bei derartigen
casts (wenn sich also nur die Signedness ändert) sich am
Bit-Pattern überhaupt nichts ändert. Es ist lediglich die
Interpretation dieses Bitpatterns die sich ändert.

Lass mich mal einen hypothetischen 4 Bit-Datentyp einführen.
Die Prinzipien sind bei größeren Datentypen absolut dieselben
nur hab ich jetzt weniger Tipparbeit :-)

Welche Bit-Pattern sind mit 4 Bit überhaupt möglich

   0000 0    0100 4    1000 8      1100 12
   0001 1    0101 5    1001 9      1101 13
   0010 2    0110 6    1010 10     1110 14
   0011 3    0111 7    1011 11     1111 15

Ich habe neben jedes Bit-Pattern auch noch die Dezimalzahl
dazugeschrieben, die diesem Bit-Pattern entspricht.
Das wären die unsigned Werte. Beginnend bei 0 wird einfach
durchgezählt, bis die Darstellungsmöglichkeiten des Bit-Patterns
aufgebraucht sind. Mit 4 Bit sind 16 verschiedene (2 hoch 4)
Zustände möglich. Demzufolge kann man mit 4 Bit die unsigned
Werte 0 bis 15 darstellen.

Wie kommt da jetzt das Vorzeichen für signed Werte ins Spiel.
Zunächst mal muss man festhalten, dass es im Rechner so was
wie ein Vorzeichen nicht wirklich gibt. Was es gibt ist eine
Konvention. Und zwar vereinbart man zb. dass wenn das am weitesten
links stehende Bit 0 ist, dass man dann die Zahl als positive
Zahl ansieht, während man die Zahl als negativ ansieht wenn das
am weitesten links stehende Bit (das sog. Most Signifikant Bit
oder MSB) eine 1 darstellt.

zb. könnte man die 16 möglichen Zustände dann auch so inter-
pretieren:

   0000 +0    0100 +4    1000 -0     1100 -4
   0001 +1    0101 +5    1001 -1     1101 -5
   0010 +2    0110 +6    1010 -2     1110 -6
   0011 +3    0111 +7    1011 -3     1111 -7

Wäre eine Möglichkeit. Man könnte auch so interpretieren:

   0000 +0    0100 +4    1000 -7     1100 -3
   0001 +1    0101 +5    1001 -6     1101 -2
   0010 +2    0110 +6    1010 -5     1110 -1
   0011 +3    0111 +7    1011 -4     1111 -0

Du siehst schon: Während bei den positiven Zahlen es relativ
naheliegend ist, wie ein Bitpattern interpretiert werden soll,
ist das bei den negativen weit weniger intuitiv.
Aber welche Interpretation soll man wählen? Was ist sinnvoll.
Nun, sinnvoll ist ganz sicher eine Interpretation, bei der
man in der tatsächlichen Rechnerei keine Probleme hat.
Und genau da kommt jetzt das 2-er Komplement ins Spiel.
Das 2-er Komplement besagt, dass man eine Zahl negiert, indem
alle Bits umgedreht werden (aus 0 wird 1, aus 1 wird 0) und dann
noch 1 addiert wird.

Beispiel: aus dem Bitpattern  0110 (welches der Zahl 6 entspricht)
wird so:
     0110         ursprüngliche Zahl
     1001         alle Bits umgedreht
     1010         und noch 1 dazugezählt.

1010 soll also das Bit-Pattern für -6 sein (weil ja die ursprüngliche
Zahl 6 war). Macht man das für alle Bit-Pattern so ergibt sich

   0000 +0    0100 +4    1000 -8     1100 -4
   0001 +1    0101 +5    1001 -7     1101 -3
   0010 +2    0110 +6    1010 -6     1110 -2
   0011 +3    0111 +7    1011 -5     1111 -1

Wieder zeigt sich, dass bei allen negativen Zahlen das MSB 1 ist,
und somit quasi die Anzeige 'Ich bin eine negative Zahl' darstellt.

Das 2-er Komplement hat die angenehme Eigenschaft, dass es in
beide Richtungen funktioniert. Nehmen wir das Bit Pattern
1011. Da das MSB 1 ist, handelt es sich um eine negative Zahl.
Um zu wissen welche, bestimmen wir einfach die dazugehörige
positive Zahl:

      Bits umdrehen     +1
    1011   ->    0100   ->   0101

0101 ist die Zahl 5. Also ist 1011 das Bit-Pattern für -5

Damit nicht genug. Das 2-er Komplement sorgt auch dafür, dass
man bei Additionen sich keine Gedanken über das Vorzeichen machen
muss.
Eine Addition:   4 + -7   erfolgt ganz normal ohne dass man für
die negative Zahl -7 irgendwelche Vorkehrungen treffen muss:

    0100
  + 1001
   -----
    1101

Im Ergebnis ist das MSB gesetzt, das Ergebnis ist also eine negative
Zahl. Welche?

     1101   ->   0010    ->  0011

0011 ist dezimal 3. Das Ergebnis ist also -3. Und das ist ja für
4 + -7 offensichtlich korrekt.

Soweit zu signed.

Wenn man jetzt aber die Tabellen für unsigned und signed
mal vergleicht:


unsigned

   0000 0    0100 4    1000 8      1100 12
   0001 1    0101 5    1001 9      1101 13
   0010 2    0110 6    1010 10     1110 14
   0011 3    0111 7    1011 11     1111 15


signed

   0000 +0    0100 +4    1000 -8     1100 -4
   0001 +1    0101 +5    1001 -7     1101 -3
   0010 +2    0110 +6    1010 -6     1110 -2
   0011 +3    0111 +7    1011 -5     1111 -1

so sieht man, dass sie für alle (signed) positiven Zahl übereinstimmen.
Signed 6 hat das gleiche Bit-Muster wie unsiged 6. Lediglich dann
wenn das MSB gesetzt ist, gibt es Unterschiede: Im unsigned Fall
handelt es sich dann um eine Zahl, die mit signed Mitteln nicht
mehr darstellbar wäre (weil dazu ja dieses eine Bit notwendig
wäre, welches die Rolle des Vorzeichens spielt). Diese Bit-Muster
werden dann im signed Fall einfach anders interpretiert.

Zurück zu deinem Beispiel und 8-Bit Zahlen:

Das Bit Muster für unsigned 120 lautet:   01111000

Da hier das MSB nicht gesetzt ist, wäre das also auch als
signed Zahl die Zahl +120

Was ist mit der unsigned Zahl 240.
Das zugehörige Bitmuster lautet: 11110000

Da hier das MSB gesetzt ist, kannst du darauf wetten, dass diese
Bit-Muster, wenn wir es als signed Zahl betrachten, nicht mehr
240 ergeben wird. MSB = 1 bedeutet ja: es handelt sich um
eine neagtive Zahl. Aber um welche?
2-er Komplement anwenden:

    11110000  -> 00001111   -> 00010000

und das entspricht wiederum der Zahl 16. Also ist 11110000 das
Bitmuster der Zahl -16, wenn dieses Bitmuster als signed Zahl
interpretiert wird. Tue ich das nicht, und möchte ich dieses
Bitmuster als unsigned sehen, dann entspricht es der Zahl 240.

signed und unsigned sind also nur 2 verschiedene Betrachtungsweisen
desselben Bitmusters. Wechsle ich von einem zum anderen, dann
ändert sich am Bitmuster selbst überhaupt nichts. Lediglich die
Aussage, die dieses Bitmuster über seine Zahl macht, ändert sich.

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.