Integer Overflow

Gast #6461606
Lesenswert?

Hallo zusammen,

Mal eine Frage an euch C cracks über integer overflows. Man nehme 
folgendes Beispiel an:
1
static uint16_t top_value = 6249;
2

3
void pwm_set(uint8_t value) {
4
  value = (value > 100) ? (100) : (value);
5
  
6
  // 1. Möglichkeit
7
  uint16_t cmp = (top_value * value) / 100;
8

9
  // 2. Möglichkeit
10
  uint16_t cmp = top_value / 100 * value;
11
}

Aus meiner Sicht wäre in der ersten Möglichkeit ein Overflow, falls der 
value z.B. 100 ist. Handelt dass der Compiler, oder muss ich da selber 
auf die zweite Möglichkeit ausweichen? Die Erste würde mir besser 
gefallen, da dann kein (oder ein viel kleinerer) Rundungsfehler 
entsteht.
Moderator Persönliche Seite #6461615
Lesenswert?

Ist die Frage, wie groß ein "int" auf deiner Maschine ist.

Ist es nur 16 Bit (bpsw. AVR), dann macht der Compiler nichts, um dem 
Overflow zu entgegnen. Du müsstest dort manuell einen der beiden 
Elemente der Multiplikatiion auf "unsigned int" casten.

Ist es 32 Bit (bspw. ARM), dann würden die Operanden implizit nach "int" 
konvertiert. Dann passt alles.

Deine zweite Variante ist natürlich suboptimal; dann könntest du auch 
gleich den Top-Wert durch 100 teilen.
Gast #6461631
Lesenswert?

Ich schlage vor, falls es um einen AVR geht, und der Compiler GCC ist, 
auf 32 Bit zu casten.
Denn dezimal 6294 hat schon 13 Bit und die Multiplikation mit dezimal 
100 (7 Bit) oder sogar dezimal 255 (8 Bit) ergibt dann 20 bwz. 21 Bit.
unsigned int hat, soweit ich weiß, hingegen nur 16 Bit.
Gast #6462118
Lesenswert?

Jörg W. schrieb:
> Deine zweite Variante ist natürlich suboptimal; dann könntest du auch
> gleich den Top-Wert durch 100 teilen.


Jo..

ooder durch zehn ;) dann ist der Rundungsfehler (value/100)
und der maximalwert 62500 (also innerhalb 16bit)

cmd = (625*value)/10
oder wenn der Rundungsfehler inakzeptabel scheint (ist ja nie grösser 
als 1!)
und da wir ja von integern reden.... zu vernachlässigen IMHO...
egal falls Deiner Meinung nach nicht:
cmd = (625*value)/10 - value/100

(lass mal den controller beides nebeneinander herrechnen, alle werte 
zwischen 1 und 100 ... ich denke es wird keinen Unterschied machen 
#schulterzuck

'sid
Moderator (Firma: Titel) Persönliche Seite #6462379
Lesenswert?

sid schrieb:
> ich denke es wird keinen Unterschied machen #schulterzuck
Bestenfalls bei der Laufzeit... ;-)

Und dann könnte man noch weiter optimieren und das Ganze auf eine 16er 
Basis stellen, so dass man mit einer Multiplikation auskommt und gar 
nicht teilen muss, sondern nur die unteren 16 Bit abschneidet:
1
  cmp = (6249 * value) / 100;
2

3
                         // den Bruch mit "65536/100" erweitern
4
      = (6249 * value * 65536/100) / (100 * 65536/100); 
5
                                      
6
                         // Unsortieren
7
      = (6249 * 65536/100 * value ) / (100/100 * 65536)
8
   
9
                         // Division durch "Shift" ersetzen
10
      = (6249 * 65536/100 * value) >> 16;
11

12
                         // nichts für schwache Nerven: Magic Number
13
      = (4095344 * value) >> 16;
Und voila: wie versprochen ist von der Division nur eine Multiplikation 
übrig... ;-)

BTW: klar, dass man in den obigen Zwischenschritten für ein korrektes 
ergebnis hie und da noch ein UL anfügen sollte.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren