Zweierpotenz im Präprozessor erkennen

#3640006
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.
Gast #3640015
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.
Gast #3640030
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?
#3640048
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.
Gast #3640418
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.
Gast #3640712
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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren