Forum: Compiler & IDEs Fehler in Integer-Berechnung


von Christian (Gast)


Lesenswert?

Moin,

ich habe hier folgende Funktion geschrieben. Urpsrünglich hatte ich sie 
für float Werte geschrieben, was auch funktioniert hat. Jetzt möchte ich 
sie auf Integer Werte umschreiben und bekomme falsche Werte. Sicherlich 
läuft ein Integer über oder so, aber ich weiss nicht wo.
1
#define PWM_TOP 1600
2
3
void SetPWM(uint8_t dutycycle)
4
{
5
  uint16_t _ocr1a = 0;
6
  // Fehlerhafte Eingabe abfangen
7
  if (dutycycle > 100) dutycycle = 100;
8
  if (dutycycle < 0)   dutycycle = 0;  
9
  
10
        //Vergleichswert berechnen
11
  _ocr1a = ((PWM_TOP*dutycycle) / 100); 
12
  
13
  sprintf(sbuf,"OCR1A: %u", _ocr1a);
14
  usb_send_str(sbuf);
15
  
16
  //Vergleichswert setzen
17
  OCR1A = _ocr1a;
18
}

Erwarten würde ich:
_ocr1a = 1600*50/100 = 80.000/100 = 800
Das Ergebnis ist:
_ocr1a = 144

Ich dachte erst, dass das Zwischenergebnis von 80.000 das Problem wäre, 
da es nicht in uint16_t passt. Aber die Berechnung mit uint32_t ergab 
das gleiche unsinnige Ergebnis.

Ich sehe meinen Fehler gerade echt nicht...

Viele Grüße,
Christian

von Sven S. (boldie)


Lesenswert?

Probiers mal so. Unsinnigen Vergleich <0 kannste weglassen und dutycycle 
must du erst umwandeln.
1
   uint16_t _ocr1a = 0;
2
   // Fehlerhafte Eingabe abfangen
3
   if (dutycycle > 100) dutycycle = 100; 
4
         //Vergleichswert berechnen
5
   _ocr1a = (PWM_TOP/100)*(uint16_t)(dutycycle);
6
 ...

von Oliver S. (oliverso)


Lesenswert?

Christian schrieb:
> Ich dachte erst, dass das Zwischenergebnis von 80.000 das Problem wäre,

Das ist auch das Problem. Die "Falle" in C ist, daß der Compiler erst 
rechnet, und danach in das Ergebnisformat umwandelt. In deinem Fall wird 
mit unsigned int (= uint16_t) gerechnet, und da passiert der Überlauf. 
Ob das falsche Ergebnis dann einem uint16_t oder einem uint32_t 
zugewiesen wird, ändert nichts mehr.

Willst du das vermeiden, musst du die Operanden casten:
1
_ocr1a = (((uint32_t)PWM_TOP*dutycycle) / 100);

Dann wird mit 32 Bit gerechnet, und alles wird gut (und dauert dafür 
länger).

Oliver

von Christian (Gast)


Lesenswert?

Stimmt der Vergleich kleiner Null macht jetzt in Festkommaarithmetik 
keinen sinn mehr ;-)

Ich hatte dutycycle auch schon im Funktionsaufruf als uint16_t 
deklariert. Das Ergebnis war das gleiche. ich Probiers nochmal mit 
deiner Rechenreihenfolge! Danke!

von Christian (Gast)


Lesenswert?

Danke Sven, mit der geänderten Berechnungsreihenfolge läufts!
Auch ohne 32Bit cast!

von Dirk B. (dirkb2)


Lesenswert?

Christian schrieb:
> mit der geänderten Berechnungsreihenfolge läufts!
> Auch ohne 32Bit cast!
Aber nur richtig, wenn PWM_TOP ein Vielfaches von 100 ist.

Oliver S. schrieb:
> In deinem Fall wird
> mit unsigned int (= uint16_t) gerechnet,
Da alle beteiligten Werte (PWM_TOP, dutycycle, 100) in ein int passen, 
erfolgt die Berechnung sogar als int.

von Oliver S. (oliverso)


Lesenswert?

Dirk B. schrieb:
> Da alle beteiligten Werte (PWM_TOP, dutycycle, 100) in ein int passen,
> erfolgt die Berechnung sogar als int.

Stimmt ;)

Oliver

von Kaj (Gast)


Lesenswert?

Christian schrieb:
> void SetPWM(uint8_t dutycycle)
> {
>   uint16_t _ocr1a = 0;
>   // Fehlerhafte Eingabe abfangen
>   if (dutycycle > 100) dutycycle = 100;
>   if (dutycycle < 0)   dutycycle = 0;
Wie soll dutycycle denn bitte kleiner 0 werden?
Tipp: void SetPWM(uint8_t dutycycle)
Was haben unsigned Datentypen fuer eine Eigenschaft?

von Dirk B. (dirkb2)


Lesenswert?

Kaj schrieb:
> Was haben unsigned Datentypen fuer eine Eigenschaft?
Das hat der TO ja schon vor über fünf Stunden eingesehen

Christian schrieb im Beitrag #3664190 am 22.05.2014 08:53:
> Stimmt der Vergleich kleiner Null macht jetzt in Festkommaarithmetik
> keinen sinn mehr ;-)

von Christian (Gast)


Lesenswert?

Richtig, das war ein Überbleibsel aus der ursprünglichen Funktion, die 
mit double gearbeitet hat.

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.