Forum: Compiler & IDEs Zweierpotenz im Präprozessor erkennen


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,
ich habe mal wieder den berühmten Präprozessor-Knoten im Kopf. Gibt es 
eine Möglichkeit, zur Compilezeit zu erkennen, ob ein #define eine 
Zweierpotenz ist (oder andersherum: Ob genau ein Bit gesetzt ist)?

Im bei 8 Bit geht natürlich einfach:
1
#define BZ 2 // muss Zweierpotenz sein!
2
3
_Static_assert(BZ == 1 || BZ == 2 || BZ == 4 || BZ == 8 || \
4
              BZ == 16 || BZ == 32 || BZ == 64 || BZ == 128,\
5
              "muss 2^n sein!");

Aber gibt es eine allgemeinere Lösung?

Zusatzfrage: Gibt es eine effiziente Laufzeit-Variante, die 
darüberhinaus noch eine effiziente Division erlaubt?

Viele Grüße
W.T.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Aber gibt es eine allgemeinere Lösung?

Der Präprozessor ist ein recht schwaches Instrument, wenn es um mehr als 
um Textersetzung geht.

Wobei man aber heute oft drauf setzen kann, dass der Compiler konstante 
Ausdrücke erkennt und zur Übersetzungszeit optimiert. Man also oftmals 
arithmetischen Fragen ganz normal in C formulieren kann.

: Bearbeitet durch User
von Uwe B. (derexponent)


Lesenswert?

1
#define  BZ  2
2
3
4
5
#define  BZ_TEST  BZ-1
6
7
#if (BZ & BZ_TEST)
8
  #error not a power of 2
9
#endif

von ah8 (Gast)


Lesenswert?

Warum muss es denn unbedingt eine Zweipotenz sein? Das muss ja seinen 
Grund haben, damit irgendetwas funktioniert. Vielleicht kann man 
versuchen, dass in einem assert nachzubilden? Wenn es keinen Fehler 
gibt, war es eine Zweierpotenz.

Oder man geht andersherum an die Sache heran:
1
#define BZ(e) (1<<(e))

Das ist garantiert eine Zweierpotenz.

von ah8 (Gast)


Lesenswert?

Uwe B. schrieb:
>
1
> #define  BZ  2
2
> 
3
> 
4
> 
5
> #define  BZ_TEST  BZ-1
6
> 
7
> #if (BZ & BZ_TEST)
8
>   #error not a power of 2
9
> #endif
10
>

Nicht schlecht! Aber ist das garantiert? Was ist zum Beispiel mit BZ=0 
oder negativen Werten? (OK, kann man vielleicht relativ einfach 
ausschließen.)

Brauche ich unbedingt das BZ_TEST? Ginge (BZ & BZ-1) in einem Ausdruck 
nicht auch?

von Uwe B. (derexponent)


Lesenswert?

mit nichts zufrieden :-)
1
#define  BZ  2
2
3
4
5
#if (BZ & BZ-1) || (BZ<=0)
6
  #error invalid value
7
#endif

...besser ?

von Walter T. (nicolas)


Lesenswert?

Danke für die schnellen Antworten!

A. K. schrieb:
> Wobei man aber heute oft drauf setzen kann, dass der Compiler konstante
> Ausdrücke erkennt und zur Übersetzungszeit optimiert. Man also oftmals
> arithmetischen Fragen ganz normal in C formulieren kann.

ah8 schrieb:
> Warum muss es denn unbedingt eine Zweipotenz sein? Das muss ja seinen
> Grund haben, damit irgendetwas funktioniert. Vielleicht kann man
> versuchen, dass in einem assert nachzubilden? Wenn es keinen Fehler
> gibt, war es eine Zweierpotenz.
>
> Oder man geht andersherum an die Sache heran:
> #define BZ(e) (1<<(e))
>
> Das ist garantiert eine Zweierpotenz.


Genau das habe ich vor. Ich habe vor einen (konstanten) Faktor als Bruch 
zu definieren, damit der Compiler eine Division in Shifts verwandeln 
kann:
1
// µm/step = µm/u / (step/u)
2
#define STEP_PER_UNIT 32  // step/u, sollte Zweierpotenz sein
3
#define MUM_PER_UNIT  10  // µm/u
Mit der Zahl hoffe ich, daß es nach einem Jahr verständlicher als als 
Exponnent ist.


Uwe B. schrieb:
> #define  BZ  2
>
> #define  BZ_TEST  BZ-1
>
> #if (BZ & BZ_TEST)
>   #error not a power of 2
> #endif

Das sieht gut aus. Ich habe auf Anhieb keinen Fall konstruieren können, 
wo das für positive Zahlen schiefgeht.


Irgendwann will ich das Ganze vielleicht noch vom Benutzer änderbar 
gestalten- dann ist es zwingend, daß es als echte Zahl angezeigt wird. 
Dann wird mir wohl nichts anderes übrig bleiben als den dualen 
Logarithmus auszurechnen, um die Division von Hand in Shifts zu 
verwandeln. Aber momentan reicht mir das hier.

Viele Grüße
W.T.

: Bearbeitet durch User
von ah8 (Gast)


Lesenswert?

Uwe B. schrieb:
> mit nichts zufrieden :-)
>
>
1
> #define  BZ  2
2
> 
3
> 
4
> 
5
> #if (BZ & BZ-1) || (BZ<=0)
6
>   #error invalid value
7
> #endif
8
>
>
> ...besser ?

Viel besser ... ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

ah8 schrieb:
> Viel besser ... ;-)

Jetzt noch in der Makro-Ersetzung "BZ" in Klammern setzen, dann ist
es perfekt. ;-)

von Kaj (Gast)


Lesenswert?

Walter Tarpan schrieb:
> damit der Compiler eine Division in Shifts verwandeln
> kann:
Und du glaubst, der Compiler schaft das nicht ohne deine Hilfe?

von Walter T. (nicolas)


Lesenswert?

Kaj schrieb:
> Und du glaubst, der Compiler schaft das nicht ohne deine Hilfe?

Jepp, das glaube ich. Der Compiler kann ja noch nicht einmal den Wert 
als Zweierpotenz definieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Was soll das nun bedeuten? Warum sollte der Compiler was definieren?

von holger (Gast)


Lesenswert?

>> Und du glaubst, der Compiler schaft das nicht ohne deine Hilfe?
>
>Jepp, das glaube ich.

Glauben kannst du in der Kirche. Ich würde eher mal
im erzeugten Assemblerlisting nachsehen ob der Glaube
dort auch bestärkt wird.

von Rolf Magnus (Gast)


Lesenswert?

holger schrieb:
> Ich würde eher mal im erzeugten Assemblerlisting nachsehen ob der Glaube
> dort auch bestärkt wird.

Manchmal reicht es auch, einfach logisch zu denken. Wie soll der 
Compiler denn eine Division durch eine nicht-Zweiterpotenz durch einen 
einfachen Shift ersetzen können?

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.