2012-12-25 12 views
13

Beachten Sie Folgendes:Boolesche Multiplikation in C++?

inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;} 
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;} 

Die Syntax von f2 ist kompakter, aber ist der Standard garantiert, dass f1 und f2 absolut gleichwertig?

Wenn ich möchte, dass der Compiler diesen Ausdruck optimiert, wenn b und i zur Kompilierzeit bekannt sind, welche Version sollte ich bevorzugen?

+2

Sie können nicht wissen, was "der Compiler" mit dem Ausdruck tun wird (im Fall von bekannten Kompilierzeitargumenten), ohne zu spezifizieren, welcher Compiler, aber es wird wahrscheinlich beide weg optimieren. Eine Möglichkeit zu sagen, kompilieren jedes mit Kompilierzeit bekannten Argumente und überprüfen Sie die Ausgabe. –

+1

Schauen Sie sich den generierten Assembly Code an – James

+1

FWIW, MSVC erzeugt eine Maske und addiert für den ersten Fall, und eine Multiplikation für die Sekunde, in einem Fall, in dem ich das Abspielen von Tricks mit der Auswertung des Booleans selbst verhinderte (wenn es kann ändert den Test, der den Bool erzeugt, um weiter zu optimieren). Im Wesentlichen ist MSVC in beiden Fällen zweiglos und scheint im naiven Fall optimaler zu sein. – JasonD

Antwort

11

Nun, ja, beide sind gleichwertig. bool ist ein integraler Typ und true ist garantiert zu 1 in ganzzahligen Kontext zu konvertieren, während false wird garantiert, um zu konvertieren 0.

(Das gilt auch umgekehrt, dh Nicht-Null-Integer-Werte sind garantiert true in Booleschen Kontext konvertieren, während Null ganzzahlige Werte garantiert werden false in Booleschen Kontext konvertieren.)

Da Sie arbeiten mit unsigned Typen, kann man leicht mit anderen kommen, möglicherweise Bit-hack-basierte noch perfekt portable Implementierungen derselben Sache, wie

i & -(unsigned) b 

obwohl ein anständiger Compiler in der Lage sein sollte, die beste Umsetzung von selbst zu wählen für jede Ihrer Versionen.

P.S. Obwohl zu meiner großen Überraschung hat GCC 4.1.2 alle drei Varianten praktisch wörtlich kompiliert, d. H. Es hat einen Maschinenmultiplikationsbefehl in einer Multiplikations-basierten Variante verwendet. Es war klug genug, cmovne Anweisung auf der ?: Variante zu verwenden, um es zweiglos zu machen, was es möglicherweise die effizienteste Implementierung machte.

1

Der Compiler wird eine implizite Konvertierung verwenden, um unsigned int von b zu machen, also, ja, das sollte funktionieren. Sie überspringen die Zustandsprüfung durch einfache Multiplikation. Welche ist effektiver/schneller? Ich weiß es nicht. Ein guter Compiler würde wahrscheinlich beide Versionen optimieren, die ich annehmen würde.

5

Ja. Es ist sicher anzunehmen, true1 ist und false ist 0 wenn in Ausdrücken wie Sie und garantiert:

C++ 11, Integral Promotions, 4,5:

Ein R-Wert vom Typ bool kann in einen rvalue vom Typ int konvertiert werden, wobei false zu null wird und true zu eins wird.

0

FWIW, der folgende Code

inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;} 
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;} 

int main() 
{ 
    volatile unsigned int i = f1(42, true); 
    volatile unsigned int j = f2(42, true); 
} 

mit gcc -O2 zusammengestellt produziert diese Versammlung:

.file "test.cpp" 
    .def ___main; .scl 2; .type 32; .endef 
    .section .text.startup,"x" 
    .p2align 2,,3 
    .globl _main 
    .def _main; .scl 2; .type 32; .endef 
_main: 
LFB2: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $16, %esp 
    call ___main 
    movl $42, 8(%esp) // i 
    movl $42, 12(%esp) // j 
    xorl %eax, %eax 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
LFE2: 

gibt es nicht mehr viel übrig von entweder f1 oder f2, wie Sie sehen können.

Soweit der C++ - Standard betroffen ist, darf der Compiler alles in Bezug auf die Optimierung tun, solange es das beobachtbare Verhalten nicht ändert (die als ob Regel).

+2

Um ein repräsentatives Ergebnis zu erzeugen, sollten Sie wahrscheinlich nicht-vorhersagbare (vom Compiler) Argumente für die Funktion 'f1 (rand(), rand())' verwenden, wäre ein besseres Beispiel. – AnT