Forum: Compiler & IDEs Potenzen in defines nicht möglich?


von Jan Mayer (Gast)


Lesenswert?

Hallo,

habe ein kleines Syntaxproblem, bzw. bekomme eine Warnung vom Compiler. 
Folgendes geht ohne Probleme/Warnings:
1
// Zeit im Klartext
2
#define MY_TIME      100    // = 100msec
3
4
// Zeiten für den uController berechnen (65536 = 2^16 = 16-Bit Timer, kein Prescaler)
5
#define START        (unsigned short) ((unsigned long) F_CPU/65536 * MY_TIME/1000)

Dies hier (für mich logischer) bringt allerdings eine Warnung:
1
// Zeit im Klartext
2
#define MY_TIME      100    // = 100msec
3
4
// Zeiten für den uController berechnen (65536 = 2^16 = 16-Bit Timer, kein Prescaler)
5
#define START        (unsigned short) ((unsigned long) F_CPU/(2^16) * MY_TIME/1000)


zugehörige Warnung die auf die (erste) Stelle der Verwendung des defines 
zeigt:

../timer-test.c:190: warning: overflow in implicit constant conversion

Was geht da schief?

Gruß,

Jans

von Benedikt K. (benedikt)


Lesenswert?

Jan Mayer wrote:

> ../timer-test.c:190: warning: overflow in implicit constant conversion
>
> Was geht da schief?

^ macht etwas anderes als du denkst...

Was du suchst ist 1<<16 anstelle von 2^16.

von Falk B. (falk)


Lesenswert?

@ Jan Mayer (Gast)

>// Zeiten für den uController berechnen (65536 = 2^16 = 16-Bit Timer, kein 
Prescaler)
>#define START        (unsigned short) ((unsigned long) F_CPU/(2^16) * 
MY_TIME/1000)

Das ist ungünstig, weil der COmpiler die Rechnung von links nach recht 
durchzieht und dadurch grössere Rundungsfehler entstehen. Prinzipiell 
immer erst alle Multiplikationen machen und dann erst alle Divisionen. 
Ausserdem, was soll der Quark mit den doppelten Casts? Und immer eine 
Klammer um alles, sonst gibts mal böse Überaschungen. Das L bei der 2 
ist wichtig, um dem Compiler zu sagen dass er mit Long rechnen soll. 
Ohne Angabe rechnert er nämlich mit int, un das macht den Overflow.

#define START (F_CPU * MY_TIME/(2L<<16 * 1000))


@ Benedikt K. (benedikt)

>^ macht etwas anderes als du denkst...

Hehe, das ist eine XOR Verknüpfung. ;-)

>Was du suchst ist 1<<16 anstelle von 2^16.

Das macht bei int aber immer noch nen Overflow.

MFg
Falk

von Benedikt K. (benedikt)


Lesenswert?

Falk Brunner wrote:

>>Was du suchst ist 1<<16 anstelle von 2^16.
>
> Das macht bei int aber immer noch nen Overflow.

Stimmt, es steht in einer Klammer, also wird es als int gerechnet, ehe 
das Ergebnis auf logn gecastet wird (oder ist das falsch) ?

von Jan Mayer (Gast)


Lesenswert?

Hallo,

autsch, da habe ich ja ganz tief ins Klo gegriffen ;=)
Ok, also ^ ist auch bei defines ein XOR. Dachte das betrachtet nur der 
Präprozessor und der erkennt ^ als Potenz. Also doch eher math.h 
einbinden und pow nutzen? Denn das 2L<<16 = 2 hoch 16 ist, ist mir jetzt 
klar, in 2 Wochen evtl. nicht mehr. Deswegen wollte ich ja auch nicht 
die 65536 dort stehen haben.
Das der Compiler von links nach rechts rechnet ist ja erwünscht, bzw. 
war mir klar. Genau deshalb habe ich erst mal dividiert, damit ich mit 
2^32 (unsigned long) auch noch hinkomme.
Den doppelten cast hielt ich für nötig, damit die Rechnung (mit den 
großen Zahlen) in unsigned long durchgeführt wird, das Ergebnis wiederum 
in ein (unsigned short) passt. Also können beide casts entfallen?
Warum eine Klammer um den ganzen define Ausdruck? Der Präprozessor 
sollte doch erst alles ausrechnen und dann zuweisen, oder?
Warum übrigens 2L<<16 und nicht 2UL<<16 (also unsigned, ist ja immer 
positiv)?

Muss da mal dringend was drüber lesen...

Danke,

Jan

von Jan Mayer (Gast)


Lesenswert?

Hi,

noch eine Frage:
müsste es statt 2UL<<16 nicht eher 1UL<<16 heißen?
Denn sonst würde ja die Bitmaske ...00010 um 16 Stellen geschoben.

Gruß,

Jan

von Hmmm... (Gast)


Lesenswert?

> Muss da mal dringend was drüber lesen...

Jo

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> müsste es statt 2UL<<16 nicht eher 1UL<<16 heißen?

Richtig. Die Angabe von Falk ist falsch.


> Denn das 2*L<<16 = 2 hoch 16 ist, ist mir jetzt
> klar, in 2 Wochen evtl. nicht mehr.

Dann solltest Du das mit dem Programmieren in C vielleicht gleich wieder 
sein lassen? Der Shift-Operator gehört zum C-Programmieren wie das Atmen 
zum Leben.


*) Natürlich ist 1L<<16 2 hoch 16 und nicht 2L<<16.

Das "L" ist übrigens nur auf 16-Bit-Systemen erforderlich, auf 
32-Bit-Systemen kann es entfallen. Das liegt an sizeof (int).

von Falk B. (falk)


Lesenswert?

@ Jan Mayer (Gast)

>Präprozessor und der erkennt ^ als Potenz. Also doch eher math.h

Nein, kein math.h.

>einbinden und pow nutzen? Denn das 2L<<16 = 2 hoch 16 ist, ist mir jetzt
>klar, in 2 Wochen evtl. nicht mehr. Deswegen wollte ich ja auch nicht

???
Alzheimer?
Das sind Grundlagen!

>Warum eine Klammer um den ganzen define Ausdruck? Der Präprozessor
>sollte doch erst alles ausrechnen und dann zuweisen, oder?

Nein, der Präprozessor rechnet gar nicht. Er ersetz nur Text.

#define sum a+b

int x;

x = 5 * sum;

Na, dämmerts?

>Warum übrigens 2L<<16 und nicht 2UL<<16 (also unsigned, ist ja immer
>positiv)?

UL geht auch.

>müsste es statt 2UL<<16 nicht eher 1UL<<16 heißen?
>Denn sonst würde ja die Bitmaske ...00010 um 16 Stellen geschoben.

Stimmt. Uuuups. Kleiner Aufmerksamkeitstest. ;-)

>Das "L" ist übrigens nur auf 16-Bit-Systemen erforderlich, auf
>32-Bit-Systemen kann es entfallen. Das liegt an sizeof (int).

Na aber mit L wirds systemunabhängig wasserdicht. Wer diesem Müll mit 
unterschielichen INT Grössen eingeführt hat sollte an die Wand gestellt 
werden.

MFg
Falk

von Jörg X. (Gast)


Lesenswert?

> Ok, also ^ ist auch bei defines ein XOR[..]
Nicht nur bei #defines -also dem Präprozessor- sondern in C allgemein. C 
kennt keinen Potenzoperator (außer eben << bei der Basis 2). Falls du 
die Potenz wirklich mal brauchst, mach es :
a) für ints als Schleife
b) es gibt "double pow(double basis, double exponent)" in der math.h 
(nur mit -lm), aber meistens braucht man kein float als Exponenten.

hth. Jörg

von Jan Mayer (Gast)


Lesenswert?

Hallo,

so und jetzt bin ich ruhig und werde mich weider meinen C Grundlagen 
widmen. Probiere ja gerade erst mal einen Timer "universell" zu machen, 
so das man später nur noch Zeit und Prescaler eintragen muss (geht 
natürlich nur in den Grenzen des Timers).
Trotzdem: danke.

Gruß,

Jan

von Rolf Magnus (Gast)


Lesenswert?

> Ok, also ^ ist auch bei defines ein XOR. Dachte das betrachtet nur der
> Präprozessor und der erkennt ^ als Potenz.

Wie bist du denn zu dieser Weisheit gelangt?

> Warum eine Klammer um den ganzen define Ausdruck? Der Präprozessor
> sollte doch erst alles ausrechnen und dann zuweisen, oder?

Wie kommst du darauf? #define macht nichts weiter als eine 
Textersetzung. Eine Art simpler Editor, für den man Anweisungen direkt 
in den Code schreibt. Wo immer der Präprozessor ein START findet (außer 
natürlich in Stringkonstanten und Kommentaren) ersetzt er es einfach nur 
durch (unsigned short) ((unsigned long) F_CPU/65536 * MY_TIME/1000) und 
gibt das dann so an den Compiler weiter.

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.