mikrocontroller.net

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


Autor: theB (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
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ß

Autor: Ansgar K. (paulderbademeister)
Datum:

Bewertung
0 lesenswert
nicht 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
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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein "10UL" in der zweiten Klammer behebt das Problem.

Oliver

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ansgar K. schrieb:

> Als erstes würde ich den Code
>
>
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.

Autor: theB (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die Antworten und die Diskussion.

Auf dem folgenden Weg funktioniert es einwandfrei. Endlich bekomme ich 
schlüssige Ergebnisse:
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.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ansgar K. (paulderbademeister)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ansgar K. (paulderbademeister)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.