Forum: Compiler & IDEs Berechnung mit großen Zahlen


von Malte S. (Gast)


Lesenswert?

Hallo,

ich habe folgendes Problem. Für eine Geschwindigkeitserkennung benötige 
ich einen Timer welcher teil einer Berechnung ist.

hier z.B. die Formel:

N = 3000000 / T_N_PLS

oder

VS = DIST * 90000 / T_VS_PLS

normal wären die Zahlen noch größer aber ich habe im Vorfeld den 
Ausdruck schon so weit wie möglich gekürzt.

N und VS sind jeweils unsigned int oder int zahlen, d.h. N (Drhezahl) 
bewegt sich zwischen 0 und 10000 U/min

VS hingegen ist eine Geschwindigkeit in Km/h also auch locker in ein 
unsigned char unter zu bringen.

Jetzt die Frage, wie wird der C Compiler (CodeVision AVR in meinem Fall) 
die Berechnung durchführen?

Bei erster Berechnung z.B.? Wenn N ein unsigned int ist, passt da ja die 
3000000 niemals rein, T_N_PLS ist aber technisch immer so bedingt das 
das Ergebnis der Rechnung immer in ein unsigned int passen muss. Bekomme 
ich da Probleme bei diesen beiden Berechnungen oder muss ich die 
Ausgabevariable dann evtl. in ein unsigned long oder double speichern um 
ausreichend platz zu haben?

Gruß
Malte.

von Jörg X. (Gast)


Lesenswert?

Du kannst ja "(long) long int", bzw int32_t (oder (u)int64_t, beide in 
<stdint.h>) verwenden (und ein L an feste Zahlen anhängen). oder du 
machts die Berechnung z.B. in Umdrehungen/sekunde, bzw. m/s

hth. Jörg

von Karl H. (kbuchegg)


Lesenswert?

Malte S. wrote:
> Hallo,
>

> Jetzt die Frage, wie wird der C Compiler (CodeVision AVR in meinem Fall)
> die Berechnung durchführen?
>
> Bei erster Berechnung z.B.? Wenn N ein unsigned int ist, passt da ja die
> 3000000 niemals rein, T_N_PLS ist aber technisch immer so bedingt das
> das Ergebnis der Rechnung immer in ein unsigned int passen muss. Bekomme
> ich da Probleme bei diesen beiden Berechnungen oder muss ich die
> Ausgabevariable dann evtl. in ein unsigned long oder double speichern um
> ausreichend platz zu haben?

Du musst zunächst mal die linke Seite einer Zuweisung von
der rechten Seite trennen. Beide haben nur bedingt
miteinander zu tun. Der Compiler macht

* Zuerst die Berechnung
* dann wird das Ergebnis der Berechnung so angepasst, dass es
  in die Ergebnisvariable links passt (falls das notwendig ist)
* dann erfolgt die Zuweisung.

Insbesondere ist wichtig: Welchen Datentyp N hat, ist für
die Berechnung von 3000000 / T_N_PLS völlig unwichtig. Für
die Berechnung selbst sind nur die Datentypen der beteilgten
Operanden interessant, welchen Datentyp die Ergebnisvariable hat,
ist zu diesem Zeitpunkt völlig uninteressant.

3000000 / T_N_PLS

3000000 hat welchen Datentyp? Der Compiler versucht es zunächst
mal mit int, bemerkt aber, dass ein int zu klein ist um so eine
Zahl aufzunehmen und weist daher dieser Zahl den Datentyp long
zu. Allerdings empfiehlt es sich bei solchen Dingen explizit
zu sein. Schreibe besser 3000000L, dann weist du selber auch,
dass das ein long ist.
T_N_PLS musst du selber wissen, was das ist.

-> die Division wird zumindest im Datentyp long ausgeführt
(wenn T_N_PLS ein unsigned long ist, dann wird 3000000 zu
unsigned long umgeformt), und das Ergebnis ist dann ebenfalls
ein long.

Damit kommt der Compiler zu Schritt 2: Wie passen linke und
rechte Seite vom Datentyp her zusammen?
links steht ein int, rechts steht ein long.
Ergo muss der Compiler irgendetwas damit machen. Er tut dies,
indem die höherwertigen Bytes des long berworfen werden und
nur soviele Bytes übrig bleiben, wie in einen int passen.

Es ist dein Problem als Programmierer, dafür Sorge zu tragen,
dass da nichts schief geht. Laut eigener Aussage sind die Werte
aber immer so, dass das Ergebnis in einen int passen wird.
Daher passiert da nichts wenn ...

... Schritt 3 ausgeführt wird:
die eigentliche Zuweisung.

Was ist mit der anderen Berechnung?
VS = DIST * 90000 / T_VS_PLS

Wieder: lass uns das Augenmerk zunächst mal nur auf die rechte
Seite richten. Da steht

DIST * 90000 / T_VS_PLS

Welches ist der Datentyp von DIST, welches ist der Datentyp
von 90000. Nun, von DIST weis ich es nicht, aber von 90000.
90000 ist ein int (der Compiler probiert bei ganzen Zahlen
immer zuerst einen int und nur wenn die Zahl da nicht hineinpassen
würde, kriegt die Zahl den Datentyp long).

DIST * 90000

Dieser Ausdruck hat das Potential überzulaufen. Ob das passieren
kann, kannst nur du wissen, das hängt von den Werten in DIST ab.
Falls DIST ein int ist, und du Gefahr eines Überlaufs läufst, dann
ist es klug, diese Berechnung im Zahlenraum long ausführen zu lassen.
Wieder: Die Berechnung findet in dem Datentyp statt, der der
'größere' der beiden Operanden ist. DIST lass ich mal in Ruhe, dafür
zwinge ich den Compiler aus den 90000 einen long zu machen: 90000L
und damit wird auch diese Multiplikation in long ausgeführt.

Zwischenergebnis / T_VS_PLS

Da das Zwischenergebnis ein long ist, wirdauch die Division
im Datentyp long ausgeführt und das Ergebnis ist wieder ein long.

Schritt 2: Passen die Datentypen links und rechts?
Rechts steht ein long, links ein unsigned char. Wiederrum ist
es dein Problem dafür zu Sorgen, dass das Ergebnis vom
Wertebereich her in einen unsigned char passt. Dem Compiler ist
das wurscht, der lässt einfach die überzähligen Bytes unter
den Tisch fallen, wenn er ...

Schritt 3: ... die Zuweisung ausführt.

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.