Forum: Mikrocontroller und Digitale Elektronik Warum diese Subtraktion


von martin (Gast)


Lesenswert?

Moin,

ich bin hier grade auf etwas gestoßen, dass mir komisch und überflüssig 
erscheint.

Es geht um folgenden Codeausschnitt:
1
short data[7];
2
char currentData;
3
4
if(i != 0 && i < 3)  
5
{
6
  if(currentData & 0x80)              // MSB des Zweierkomplements vorhanden ?
7
  {    
8
    data[i] = data[i] + (currentData - 256);  // Subtraktion von der Gesamtsumme
9
  }
10
  else
11
    data[i] = data[i] + currentData;      // Addition zur Gesamtsumme
12
}

Dieser Code ist aus einem Programm für einen PIC18F2520 und es wurde ein 
HITECH PICC18 Compiler benutzt.

Ich soll nun erstmal das Programm für neue Hardware mit einem AVR 
portieren.

currentDara ist ein char (nicht unsigned) und wird von einer 8 Bit 
Software SPI geselen, welche einen unsigned char zurück gibt.

Das Array data wird iteriert (i Laufindex). Ich hoffe ich nehme richtig 
an, dass ein short genau wie ein int 16 Bit beträgt.

In den Felder data[1] und data[2] wird eine Summe gebildet. Da 
currentData auch negativ sein kann (Zweierkomplement) wird hier per 
if-else geprüft ob es positiv oder negativ ist.

Im positiven Fall wird einfach addiert. Im negativen Fall wird erstmal 
der Wert 256 abgezogen und dann addiert.

Warum wird 256 abgezogen? Normal sollte eine Addition doch ausreichen 
und das if-else ist überflüssig.
Übersehe ich den Grund für diesen Umweg über das if-else und die 
Subtraktion oder ist dieses gebilde wirklich überflüssig und kann durch 
data[i] += currentData ersetzt werden?

von Thomas K. (muetze1)


Lesenswert?

Er prüft ob das MSB gesetzt ist, was eine negative Zahl anzeigt. Wenn 
diese erkannt wurde (Bit gesetzt), will er den Wert binär invertieren um 
dadurch die entsprechende Ziffer zu erhalten als Positivzahl. Dieses 
binäre invertieren erreicht er nun durch die Subtraktion (Alternative).

von Matthias F. (flint)


Lesenswert?

vorzeichen erhalten wenn der 8 bit wert in einen 16 bit wert gewandelt 
wird? kommt mir aber hatschert vor.

lg
m

von Vlad T. (vlad_tepesch)


Lesenswert?

poste doch mal die ganze Routine.

Das ganze sieht sowiso recht komisch aus

von martin (Gast)


Lesenswert?

Ich hab mir mal folgendes im Simulator angeguckt
1
  char foo = 0xFF;
2
  char bar = 100;
3
  PORTA = bar;
4
  bar += foo; //Ergebnis 99
5
  PORTB = bar;
6
  bar = bar + (foo - 256); //Ergebnix 98
7
  PORTC = bar;
Da sehe ich keinen unterschied, ob ich die negative Zahl einfach 
addiere, oder erst 256 abziehe.

von Peter D. (peda)


Lesenswert?

martin schrieb:
> Warum wird 256 abgezogen? Normal sollte eine Addition doch ausreichen
> und das if-else ist überflüssig.

Vermutlich wird damit ein Bug des Compilers korrigiert.

Du hast recht, bei einem funktionierenden Compiler ist dieser Code 
überflüssig, er macht automatisch die vorzeichenrichtige Erweiterung 
einer 8Bit-Zahl auf 16 Bit.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Vielleicht ist auf diesem Compiler aber auch einfach nur ein char als 
Default ein unsigned char.

Wie ich immer sage:
  char  ausschliesslich für Texte reservieren

In allen anderen Fällen immer explizit sein. Entweder signed oder 
unsigned char benutzen.

von martin (Gast)


Lesenswert?

OK was ich auch seltsam finde ist folgendes:
1
  unsigned char foobar = 0xFF;
2
  char foo = foobar;
3
  short bar = 100;
4
  PORTA = bar>>8;
5
  bar += foo;
6
  PORTB = bar>>8;
7
  bar = bar + (foo - 256);
8
  PORTC = bar>>8;
PORTA -> 0x00
PORTB -> 0x01
PORTC -> 0x01

Wo kommen die Einsen her?

von (prx) A. K. (prx)


Lesenswert?

Der Code funktioniert ohnehin nur, wenn "char" vorzeichenlos ist. Wer 
davon unabhängig sein will sollte ein &0xFF einbauen.

von (prx) A. K. (prx)


Lesenswert?

martin schrieb:

> Wo kommen die Einsen her?

100 + 255 = 356 = 0x16D
0x16D >> 8 = 1

356 + (255 - 256) = 355. Dito.

von Juergen (Gast)


Lesenswert?

Ich vermute mal char ist per default unsigned, und der Schreiber moechte 
den Wert als signed char zu data[i] dazuaddieren.

Das wird schief gehen, wenn dieser Code mal auf ein char trifft, das per 
default signed ist.

Richtig waere eine Deklaration von currentData als signed char, dann ist 
das eine einfache Addition.

Juergen

von (prx) A. K. (prx)


Lesenswert?

Juergen schrieb:

> Richtig waere eine Deklaration von currentData als signed char, dann ist
> das eine einfache Addition.

Auch das setzt aber voraus, dass ein char aus 8 Bits besteht.

von martin (Gast)


Lesenswert?

Also wird hier einfach ein char als unsigned char interpretiert und 
daher die 0xFF als 255 und nicht als -1?

Wenn ich aber
1
  unsigned char foobar = 0xFF;
2
  char foo = foobar;
3
  short bar = 100;
4
  PORTA = bar&0xFF;
5
  bar += foo;
6
  PORTB = bar&0xFF;
7
  bar = bar + (foo - 256);
8
  PORTC = bar0xFF;
schreibe, dann passt es
PORTA -> 0x64
PORTB -> 0x63
PORTC -> 0x62

von martin (Gast)


Lesenswert?

Meint ihr das sollte so funktionieren? (avr-gcc)
1
int16_t data[7];
2
int8_t currentData;
3
4
currentData = (int8_t)spi_read(); // spi_read gibt einen uint8_t zurück
5
6
if(i != 0 && i < 3)  
7
{
8
  data[i] += currentData;
9
}

von Karl H. (kbuchegg)


Lesenswert?

martin schrieb:
> Meint ihr das sollte so funktionieren? (avr-gcc)
>
1
> int16_t data[7];
2
> int8_t currentData;
3
> 
4
> currentData = (int8_t)spi_read(); // spi_read gibt einen uint8_t zurück
5
> 
6
> if(i != 0 && i < 3)
7
> {
8
>   data[i] += currentData;
9
> }
10
>

Ich denke, dass genau das die ursprüngliche Absicht war.
(Kaum macht man es richtig, werden viele seltsam kryptischen Codestellen 
oftmals ganz einfach :-)

von martin (Gast)


Lesenswert?

Danke, dann werde ich das so benutzen und mich weiter durch einen 
Haufen, nicht gut wartbaren, Code kämpfen.

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.