Forum: Compiler & IDEs Signed integer overflow


von Walter T. (nicolas)


Lesenswert?

Guten Abend zusammen,
ich sehe gerade wieder den Wald vor lauter Bäumen nicht mehr. Ich habe 
von einer Peripherie (TMC222) einen Zähler (16Bit, Zweierkomplement), 
bei dem der Überlauf spezifiziert ist. Da mir der Zählbereich nicht 
ausreicht, wird er regelmäßig gepollt und in einer 24-Bit-Zählvariablen 
mitgeschrieben. Und so sieht der Quelltext aus:
1
static int24_t stepact;   // Schrittvariable
2
static int16_t pos_last = 0; // Hilfsvariable
3
void tmc222_poll(void) {
4
  int16_t pos_act =  tmc222_GetPosition();
5
  int16_t diff;
6
  
7
  if (pos_act <0 ) {
8
    if (pos_last < 0) diff = pos_act-pos_last;  // kein Overflow
9
    else              diff = (pos_act-INT_MIN)-(pos_last-INT_MAX);
10
  }
11
  else {
12
   if (pos_last < 0)  diff = (pos_act-INT_MAX)-(pos_last-INT_MIN);
13
   else               diff = pos_act-pos_last;  // kein Overflow
14
  }
15
  
16
  pos_last = pos_act;
17
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
18
    stepact += diff;
19
  }    
20
}
Ich bin mir momentan nur nicht sicher, ob ich den Integer-Overflow so 
richtig berücksichtigt habe.

Viele Grüße
W.T.

von Hans (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Ich bin mir momentan nur nicht sicher, ob ich den Integer-Overflow so
> richtig berücksichtigt habe.

Nein, hast Du nicht. Beispiel: Der Zähler springt um einen Schritt von 
32767 auf -32768. Die Differenz sollte 1 sein.

Also:
pos_last = 32767
pos_act = -32768
diff = (pos_act-INT_MIN)-(pos_last-INT_MAX)
diff = (-32768-(-32768))-(32767-32767) = 0

Schon mal falsch.

Ansonsten noch ein paar Unsauberkeiten bei der Implementierung:
- Statt INT_MIN und INT_MAX solltest Du INT16_MIN und INT16_MAX nehmen, 
sonst funktioniert der Code auf einer 32-Bit-Plattform nicht.
- int24_t ist kein Standarddatentyp. Für bessere Portabilität wäre 
int32_t zu empfehlen.
- Das Verhalten beim Signed-Überlauf ist vom C-Standard nicht 
festgelegt. Der Ausdruck -(-32768) passt nicht in 16 Bit und kann daher 
theoretisch alles mögliche ergeben. Deshalb sollte da für die Berechnung 
ein Cast auf int24_t oder int32_t rein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hans schrieb:
> Das Verhalten beim Signed-Überlauf ist vom C-Standard nicht festgelegt.

Genauer: er erzeugt undefiniertes Verhalten.  Man kann sich also auf
nichts dabei verlassen.

Wenn man mit Überlauf arbeiten will, dann unsigned, da ist das alles
wohl definiert.  Ggf. danach auf signed der gleichen Größe casten.

von Fabian O. (xfr)


Lesenswert?

Jörg Wunsch schrieb:
> Wenn man mit Überlauf arbeiten will, dann unsigned, da ist das alles
> wohl definiert.  Ggf. danach auf signed der gleichen Größe casten.

Das dürfte die einfachste Lösung sein. Funktioniert dann halt "nur" auf 
Prozessoren, die mit 2er-Komplement rechnen, aber das ist wohl zu 
verkraften. ;-)
1
static int24_t  stepact;      // Schrittvariable
2
static uint16_t pos_last = 0; // Hilfsvariable
3
4
void tmc222_poll(void) {
5
  uint16_t pos_act = tmc222_GetPosition();
6
  uint16_t diff = pos_act - pos_last;  
7
  pos_last = pos_act;
8
  
9
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
10
    stepact += (int16_t) diff;
11
  }
12
}

von Walter T. (nicolas)


Lesenswert?

Guten Abend,
naja, der Überlauf kommt ja von einem Stück Peripherie, wo genau dieser 
im Datenblatt steht.

Und GCC soll ihn ja nur detektieren. Soweit paßt also schon alles.

Jörg Wunsch schrieb:
> Wenn man mit Überlauf arbeiten will, dann unsigned

Ich habe auch schon überlegt, ob ich nicht erst das Ganze + (-INT_MIN) 
nehme und in einen signed integer caste. Probiere ich morgen doch direkt 
mal aus. Ich weiß auch nicht mehr genau, warum mir das nicht gefiel. 
Wahrscheinlich weil dann noch eine 24-Bit-Variable mehr mit im Spiel 
ist.

Viele Grüße
W.T.

von Fabian O. (xfr)


Lesenswert?

Sehe gerade, man braucht den Cast nicht mal:
1
static int24_t  stepact;      // Schrittvariable
2
static uint16_t pos_last = 0; // Hilfsvariable
3
4
void tmc222_poll(void) {
5
  uint16_t pos_act = tmc222_GetPosition();
6
  int16_t diff = pos_act - pos_last;  
7
  pos_last = pos_act;
8
  
9
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
10
    stepact += diff;
11
  }
12
}

von Rolf Magnus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Wenn man mit Überlauf arbeiten will, dann unsigned, da ist das alles
> wohl definiert.  Ggf. danach auf signed der gleichen Größe casten.

Noch eine Korinthe für zwischendurch: Laut ISO C ist das 
Überlaufverhalten bei unsigned, daß es keinen Überlauf gibt. Stattdessen 
wird immer modulo Maximalwert+1 gerechnet.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dieses Verhalten kann man mit GCC auch für signed bekommen, indem man 
-fwrapv setzt.

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.