Forum: Compiler & IDEs C: Addition ohne Übertrag


von Marc (Gast)


Lesenswert?

Hallo Leute!

Ich möchte in meinem Code gern eine einfache Prüfsumme über 3 Byte 
berechnen. Alles soweit kein Problem!

Aber jetzt kommts:  0xFF + 0x01 ist beim anschliesenden Test != 0x00 
wegen dem Carry.

Gibt es eine Möglichkeit in C ohne Übertrag zu addieren?


MfG Marc

von Karl H. (kbuchegg)


Lesenswert?

Marc schrieb:

> Aber jetzt kommts:  0xFF + 0x01 ist beim anschliesenden Test != 0x00
> wegen dem Carry.

sicher nicht wegen dem Carry.

Eher schon weil du die falschen Datentypen benutzt hast, bzw. dir die 
angewendeten C-Regeln nicht klar sind.

> Gibt es eine Möglichkeit in C ohne Übertrag zu addieren?

Zeig deinen Code.

Schuss ins Blaue: Deine Berechnung wird im Zahlenraum int durchgeführt, 
während du eigentlich unsigned char haben willst. Mit einem Cast sollte 
sich das Problem beheben lassen.

von Marc (Gast)


Lesenswert?

Code geht leider grad nicht, bin nicht am Entwicklungsrechner.

Der Zahlenraum soll natürlich unsigned char sein. Ich habs mit uint8_t 
gemacht, was ja eigentlich stimmen müsste!?

Und es liegt am Carry, da im erzeugten ASM Code immer erst die normale 
Addition und dann nochmal die Carry-Addition durchgeführt wird. Beim 
anschliessenden Test zeigt der ASM Code, dass zwar das Ergebnis ==0x00 
ist, aber die Carryflags unterschiedlich sind und der Test somit negativ 
ist.

von Sven P. (Gast)


Lesenswert?

Hier funktionierts.

Ich glaub, du musst nochmal über Typenerweiterung nachdenken :->

von Marc (Gast)


Lesenswert?

Hier der Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h> 
3
#include <stdint.h>
4
#include <util/delay.h>
5
6
#include "USI_TWI_Slave.h"
7
8
#define SLAVE_ADDRESS 0x10
9
10
uint8_t command, value, checksum;
11
12
13
14
void Process_Data(void)
15
{
16
  if((SLAVE_ADDRESS + command + value) == checksum)
17
  {
18
.
19
.
20
.

Wo funktionierts??

von Detlev T. (detlevt)


Lesenswert?

Ich vermute, du speicherst die Prüfsumme nich in einer uint8_t Variable 
vor dem Vergleich, sondern machst den direkt. Berechnungen werden in C 
im Gültigkeitsbereich von int durchgeführt. Du könntest aber die unteren 
8 Bit mit einem binären UND maskieren.

von Floh (Gast)


Lesenswert?

Marc schrieb:
> Und es liegt am Carry, da im erzeugten ASM Code immer erst die normale
> Addition und dann nochmal die Carry-Addition durchgeführt wird. Beim

Also macht er die Karl-Heinz schon erwähnt hat die Berechnung auf 16-Bit 
Basis.
:-)

von Marc (Gast)


Lesenswert?

Und hier gleich mal noch der Assemblercode:
1
@00000030: Process_Data
2
45:       {
3
+00000030:   91400086    LDS       R20,0x0086     Load direct from data space
4
+00000032:   91800089    LDS       R24,0x0089     Load direct from data space
5
+00000034:   E090        LDI       R25,0x00       Load immediate
6
+00000035:   0F84        ADD       R24,R20        Add without carry
7
+00000036:   1D91        ADC       R25,R1         Add with carry
8
+00000037:   9640        ADIW      R24,0x10       Add immediate to word
9
+00000038:   91200088    LDS       R18,0x0088     Load direct from data space
10
+0000003A:   E030        LDI       R19,0x00       Load immediate
11
+0000003B:   1782        CP        R24,R18        Compare
12
+0000003C:   0793        CPC       R25,R19        Compare with carry
13
+0000003D:   F4F9        BRNE      PC+0x20        Branch if not equal

von Sven P. (Gast)


Lesenswert?

Idiotensicher geklammert:
1
if ( ((uint8_t) (SLAVE_ADDRESS + command + value)) == checksum)

von Stefan (Gast)


Lesenswert?

Detlev T. schrieb:
> Ich vermute, du speicherst die Prüfsumme nich in einer uint8_t Variable
> vor dem Vergleich, sondern machst den direkt. Berechnungen werden in C
> im Gültigkeitsbereich von int durchgeführt. Du könntest aber die unteren
> 8 Bit mit einem binären UND maskieren.

Dem würde ich mich anschließen. Das nennt sich übrigens "integral 
promotion".
IAR hat einen schönen Beitrag über das Problem gemacht:

http://supp.iar.com/Support/?note=12582&from=search+result

von Karl H. (kbuchegg)


Lesenswert?

Marc schrieb:

>   if((SLAVE_ADDRESS + command + value) == checksum)

dir ist hoffentlich klar, dass der linke Teil (die Berechnung) als int 
Berechnung gemacht wird, laut C-Spezifikation

Damit wird auch der rechte Teil auf int hochgehoben und der Vergleich 
auch als int Vergleich gemacht.

> Und es liegt am Carry

Es liegt eben nicht am Carry. In C gibt es gar keinen Carry.
Es liegt daran, dass du fälschlicherweise annimmst, dass 2 unsigned char 
zusammenaddiert wieder einen unsigned char ergeben. Dem ist aber nicht 
so!

Berechnungen werden in C niemals in einem kleineren Datentyp als dem 
Datentyp int gemacht (*). In diesem Sinne sind
1
  if((SLAVE_ADDRESS + command + value) == checksum)

und
1
  uint8_t summ = SLAVE_ADDRESS + command + value;
2
  if(summ  == checksum)
3
    ...

2 völlig verschiedene Paar Schuhe!


(*) Es sei denn, der Optimizer kann beweisen, dass sich das Ergebnis 
nicht verändert, wenn er (bei einem 16 Bit int) das High-Byte ignoriert 
und nicht berechnet. Bei dir unterscheiden sich aber die Ergebnisse, 
daher muss die Berechnung als int-Berechnung gemacht werden.

von Marc (Gast)


Lesenswert?

Jetzt ist mir alles klar! Vielen Dank für die Antworten! Wieder was 
gelernt :-)

Welche Variante erzeugt (vermutlich) weniger Code?
Die von Sven P. oder die von Karl Heinz?

von Karl H. (kbuchegg)


Lesenswert?

Marc schrieb:

> Welche Variante erzeugt (vermutlich) weniger Code?
> Die von Sven P. oder die von Karl Heinz?

Im Zweifelsfall im Assemblercode ansehen.
Bei aktiviertem Optimizer erwarte ich aber keinen Unterschied.

von Marc (Gast)


Lesenswert?

Ok vielen Dank!

von Detlev T. (detlevt)


Lesenswert?

Eventuell ergibt das ja kürzeren Code:
1
uint8_t summ = SLAVE_ADDRESS;
2
  summ += command;
3
  summ += value;
4
  summ -= checksum;
5
  if(summ == 0)

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.