mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Rechnen mit großen Integerzahlen im AVR


Autor: TheK (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie der Titel bereits sagt, kämpfe ich derzeit mit sehr großen 
Integerzahlen im AVR. Hierbei werden zwei variable, vierstellige Zahlen 
geteilt und anschließend mit einer weiteren konstanten, vierstelligen 
Zahl multipliziert.
Einen Überlauf erhalte ich bei folgendem Ausdruck (der mit 32bit kein 
Problem darstellen würde):
(unsigned int) 1234*5678/4711
Warnung: Ganzzahlüberlauf in Ausdruck
Eine andere Sortierung würde auf diesem Weg natürlich den Overflow 
vermeiden, aber wegen Rundung zu einer 0 führen:
(unsigned int) 1234/4711*5678

Also sind beide Variante keine Lösung.

Der Faktor 5678 ist hierbei die konstante Zahl, die beiden anderen 
können variieren. Die 1234 ist stets vierstellig, während die Zahl im 
Nenner (hier 4711) zwischen 3 und 5 stellig variiert.
Prinzip: E = (v1*k)/v2

Gibt es eine geschickte Methode solch große Zahlen im AVR zu berechnen?

Autor: Moi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was spricht gegen uint_32t?

Autor: Fragezeichen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum nicht

(unsigned long) 1234*5678/4711

Autor: TheK (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahja, das sollte ich vielleicht erwähnen. Mit uint32_t habe ich bereits 
herumgebastelt, bekomme aber ebenfalls den Überlauf gemeldet (was doch 
eigentlich nicht sein kann/darf):
#include <stdint.h>
uint32_t tmp;
tmp = 2075*5184/3000;

Ich verwende einen ATMega168.

Autor: nicht Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TheK schrieb:
> Ahja, das sollte ich vielleicht erwähnen. Mit uint32_t habe ich bereits
> herumgebastelt, bekomme aber ebenfalls den Überlauf gemeldet (was doch
> eigentlich nicht sein kann/darf):
> #include <stdint.h>
> uint32_t tmp;
> tmp = 2075*5184/3000;
> Ich verwende einen ATMega168.

doch wenn 2075 5184 oder 3000 nicht uint32_t sind. Probiere mal das:
tmp = (uint32_t)2075*5184/3000;

dann weiß der Compiler das er 2075 erstmals aufblasen muss bevor er 
damit rechnen soll.

Autor: TheK (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das funktioniert leider auch nicht.
Momentan teste ich auch mit fixen Werten (exakt wie oben angegeben) und 
greife noch auf gar keine Variablen zu.

Zum Test habe ich mir ein weiteres Programm erstellt, was die gleiche 
Funktionalität für mein Computer umsetzen soll. Dort verwende ich 
normales unsigned int (oder auch uint32_t) und mit dem gcc-Compiler 
läuft alles fehlerfrei durch.
Mit avr-gcc meldet er sofort wieder den Überlauf. Ist in dem Compiler 
das 8bit-System direkt schon berücksichtigt?! Muss ich beim Compilieren 
noch irgendetwas berücksichtigen?

Das kann doch echt net wahr sein... grmpf


Im übrigen scheint er mit 16bit, also uint16_t, keine Probleme zu haben, 
denn das habe ich schon seit längerem im Code drin und funktioniert 
auch.

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

Bewertung
0 lesenswert
nicht lesenswert
TheK schrieb:
> Das funktioniert leider auch nicht.
> Momentan teste ich auch mit fixen Werten (exakt wie oben angegeben) und
> greife noch auf gar keine Variablen zu.

Herzeigen.
Und zwar Copy&Paste und nicht abtippen.

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

Bewertung
0 lesenswert
nicht lesenswert
TheK schrieb:
> Ahja, das sollte ich vielleicht erwähnen. Mit uint32_t habe ich bereits
> herumgebastelt, bekomme aber ebenfalls den Überlauf gemeldet (was doch
> eigentlich nicht sein kann/darf):

Doch das kann sein.

>
>
#include <stdint.h>
> uint32_t tmp;
> tmp = 2075*5184/3000;

Es spielt absolut keine Rolle, ob du das Ergebnis in einem uint32_t 
speicherst oder nicht.

2075 ist ein int
5184 ist ein int

Also kommt hier eine int mal int Multiplikation zum Einsatz.


Es ist immer das Gleiche: Du brauchst ein C-Buch. Da sind all diese 
Regeln und noch viel mehr ausführlichst beschrieben.

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nicht Gast schrieb:
> tmp = (uint32_t)2075*5184/3000;

TheK schrieb:
> Das funktioniert leider auch nicht.

Doch. Und tmp muss dabei nicht einmal eine 32-Bit-Variable sein, wenn
das Ergebnis der Rechnung auch in 16 Bit passt.

> Im übrigen scheint er mit 16bit, also uint16_t, keine Probleme zu haben

Beim AVR-GCC ist ein int 16 Bit breit, d.h. alle Integerrechnungen
werden normalerweise ebenfalls mit 16 Bit ausgeführt. 32-Bit-Arithmetik
kommt nur dann zur Anwendung, wenn bei einer Operation mindestens einer
der Operanden 32 Bit breit ist. Im Beispiel von "nicht Gast" wird die
2075 in ein uint32_t gecastet. Somit wird die Multiplikation 2075*5184
mit 32-Bit-Arithmetik gerechnet, was ein 32-Bit-Ergebnis liefert¹.
Dieses Ergebnis ist der linke Operand der nun folgenden Division, so
dass diese ebenfalls mit 32 Bit gerechnet wird.

Auf dem PC ist ein int aber 32 Bit breit, so dass der Ausdruck auch ohne
Casterei in 32 Bit gerechnet wird.

¹) Dasselbe würde geschehen, wenn man die 2075 als long-Wert also 2075L
   oder als unsigned-long-Wert (2075UL) schreibt.

Autor: TheK (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zunächst einmal vielen Dank für Eure Antworten. Er rechnet nun korrekt, 
indem ich bei den großen Int-Werten ein UL angefügt habe.
#define F_CPU 3686400UL



#include <stdlib.h>

#include <stdint.h>

#include <avr/io.h>


uint32_t tmp=0;

int main(void) {
  tmp = 2075UL*5184UL/3000;
  while(1) {
    //
  }
  return 0;
}

Ein gutes C-Buch ist immer hilfreich, da gebe ich dir recht. Leider wird 
in den meisten Büchern eben genau die hier gegebene Problematik nicht, 
oder nur sehr wenig, behandelt.

Die Rechnung funktioniert übrigens immer noch, wenn 2075UL und 3000 
durch uint16_t-Variablen ausgetauscht werden.

Danke!

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TheK schrieb:
> Die Rechnung funktioniert übrigens immer noch, wenn 2075UL und 3000
> durch uint16_t-Variablen ausgetauscht werden.
Klar, denn dann ist trotzdem ein long (5184UL) beteiligt und die ganze 
Rechnung wird long ausgeführt...

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Entwickler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nimm anstatt 'UL' besser nur 'L'. Sobald irgendeine Zahl negativ werden 
kann, geht sonst wieder etwas schief. Dass der Wertebereich halbiert 
wird, sollte das geringere Problem sein.

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.