Forum: Compiler & IDEs ATMega168 Multiplikation/Division großer INT


von theB (Gast)


Lesenswert?

Hallo allerseits,

ich muss in einem ATMega168 mit recht großen Zahlen rechnen.
Irgendwie scheint das aber nicht richtig zu funktionieren, denn entweder 
kann er diese Zahlen nicht richtig berechnen oder die Berechnung 
erfordert zu viele Rechenzyklen. Jedenfalls kommen am Ende nicht 
nachvollziehbare Ergebnisse heraus.

Pseudocode der Rechnung:
1
uint32_t erg = (uint16_t "2000" * 5003UL) / (10 * uint16_t "50000")
Die Zahlen in Anführungsstriche stehen für beispielhafte Werte, die die 
Variablen an der Stelle annehmen können. Zusätzlich habe ich den Typ der 
jeweiligen Variable angegeben. Die Werte 5003UL und 10 sind in der 
Gleichung konstant.

Insgesamt komme ich also auf Berechnungen der Größenordnung 10.000.000 / 
500.000

Meine Frage ist nun,
- ob die Wahl der Datentypen korrekt ist?
- wie lange eine Division solch großer Zahlen schätzungsweise dauert?

Gruß

von Ansgar K. (paulderbademeister)


Lesenswert?

Falls ich mich nicht irre, wird diese Berechnung einfach als uint16 
ausgeführt und danach in einer uint32 gespeichert. Daher kommt es zu 
Überläufen, obwohl uint32 die richtige Wahl ist (Zahlen über 10^9 
möglich).

Als erstes würde ich den Code
1
uint32_t erg = (uint32_t) (Var1 * 5003) / (10 * Var2)

testen. Dieser sollte zumindest korrekte Ergebnisse liefern.
Danach wäre der nächste Schritt zu überlegen, ob man die Rechnung 
sinnvoll umformulieren könnte.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> (10 * uint16_t "50000")

Wird als uint16_t Teilergebnis gerechnet und gibt als Ergebnis 500000 
einen Overflow. Damit wird auch (uint16_t "2000" * 5003UL) / 
Teilergebnis falsch.

von Oliver (Gast)


Lesenswert?

Ein "10UL" in der zweiten Klammer behebt das Problem.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Ansgar K. schrieb:

> Als erstes würde ich den Code
>
>
1
uint32_t erg = (uint32_t) (Var1 * 5003) / (10 * Var2)
>
> testen. Dieser sollte zumindest korrekte Ergebnisse liefern.

Tut er aber nicht.

Wenn
   Var1 * 5003
einen Überlauf produziert hat, dann ist das Kind bereits in den Brunnen 
gefallen, egal ob du nachher dieses falsche Ergebnis noch auf 32 Bit 
aufbläst oder nicht.

Es ist völlig irrelevant für die Berechnung, was man mit dem Ergebnis 
einer Berechnung weiter macht.

2 Stück 16 Bit int werden mit einer 16*16 Bit Multiplikation miteinander 
multipliziert und liefern ein 16 Bit Ergebnis. Selbst dann, wenn ich 
nachher dieses 16 Bit Ergebnis in einer 32 Bit Variablen abspeichere 
oder wie hier auf 32 Bit hochcaste.

von theB (Gast)


Lesenswert?

Vielen Dank für die Antworten und die Diskussion.

Auf dem folgenden Weg funktioniert es einwandfrei. Endlich bekomme ich 
schlüssige Ergebnisse:
1
uint32_t = (uint32_t * 5003UL) / (10 * uint32_t)

Nachteil ist natürlich, dass die beiden Variablen als uint32_t 
deklariert werden, obwohl ein uint16_t ausreichen würde. Aber der µC hat 
es gefressen und es funktioniert :)

Das Umstellen der Formel ist natürlich die naheliegenste Variante, 
allerdings ist die o.g. schon deutlich vereinfacht und lässt sich nicht 
weiter aufteilen oder zusammenfassen.

von Peter (Gast)


Lesenswert?

Wie Oliver oben schon geschreiben hat: Wenn die Konstanten 10 und 5003 
jeweils uint32_t sind reicht das auch, die Variablen werden dann 
automatisch vor der Rechnung erweitert.

uint32_t erg = (Var1 * (uint32_t)5003) / ((uint32_t)10 * Var2)
bzw.
uint32_t erg = (Var1 * 5003UL) / (10UL * Var2)

Die Variablen können weiterhin 16 Bit haben.

Peter

von Ansgar K. (paulderbademeister)


Lesenswert?

Karl heinz Buchegger schrieb:
> Tut er aber nicht.

*Kopf -> Wand*
Natürlich, hast vollkommen recht. Mein Fehler, allerdings an ner ganz 
anderen Stelle:

Ich war davon ausgegangen, dass das casten des ersten Wertes ausreichen 
würde, um daraus eine uint32-Berechnung zu machen. Natürlich weder 
nachgeguckt, welcher Operator stärker bindet, noch daran gedacht, dass 
die Multiplikation des Divisors schon überlaufen könnte.

von Ansgar K. (paulderbademeister)


Lesenswert?

Oh je, ich schlafe echt noch. Bitte den zweiten Absatz des Posts oben 
ignorieren, ich kann ihn leider nicht mehr editieren. Der cast gehört 
natürlich in die Klammer rein, da er sonst erst multipliziert und dann 
castet. Ugh.

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.