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


von TheK (Gast)


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):
1
(unsigned int) 1234*5678/4711
1
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:
1
(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?

von Moi (Gast)


Lesenswert?

Was spricht gegen uint_32t?

von Fragezeichen (Gast)


Lesenswert?

Warum nicht

(unsigned long) 1234*5678/4711

von TheK (Gast)


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):
1
#include <stdint.h>
2
uint32_t tmp;
3
tmp = 2075*5184/3000;

Ich verwende einen ATMega168.

von nicht Gast (Gast)


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.

von TheK (Gast)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

>
>
1
#include <stdint.h>
2
> uint32_t tmp;
3
> 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.

von Yalu X. (yalu) (Moderator)


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.

von TheK (Gast)


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.
1
#define F_CPU 3686400UL
2
3
4
5
#include <stdlib.h>
6
7
#include <stdint.h>
8
9
#include <avr/io.h>
10
11
12
uint32_t tmp=0;
13
14
int main(void) {
15
  tmp = 2075UL*5184UL/3000;
16
  while(1) {
17
    //
18
  }
19
  return 0;
20
}

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!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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...

von Falk B. (falk)


Lesenswert?


von Entwickler (Gast)


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.

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.