Wie kann ich logische Implikation mit bitweisem oder anderem effizientem Code in C implementieren?

7

Ich möchte eine logische Operation implementieren, die so effizient wie möglich funktioniert. Ich brauche diese Wahrheitstabelle:

%Vor%

Dies wird laut Wikipedia " logische Implikation "

genannt

Ich habe lange versucht, herauszufinden, wie man dies mit bitweisen Operationen in C macht, ohne Bedingungen zu verwenden. Vielleicht hat jemand ein paar Gedanken dazu.

Danke

    
alvatar 21.03.2009, 02:55
quelle

5 Antworten

7

FYI, mit gcc-4.3.3:

%Vor%

Gibt (von objdump -d):

%Vor%

Also, keine Zweige, aber doppelt so viele Anweisungen.

@litb: Ok, hier ist mit _Bool:

%Vor%

Es ist also eine gute Idee, _Bool anstelle von int zu verwenden.

    
derobert 21.03.2009, 03:52
quelle
10
%Vor%

ist viel schnell. ernsthaft, mach dir keine Sorgen darüber.

    
eduffy 21.03.2009 02:59
quelle
10
%Vor%

Zur Visualisierung:

%Vor%

In tightem Code sollte dies schneller als "! p || q" sein, weil letzterer eine Verzweigung hat, die aufgrund eines Verzweigungsvorhersagefehlers einen Stillstand in der CPU verursachen könnte. Die bitweise Version ist deterministisch und kann als Bonus 32 Mal so viel in einer 32-Bit-Ganzzahl arbeiten als die boolesche Version!

    
Chris Dolan 21.03.2009 03:00
quelle
4

Sie können auf Boolesche Ausdrücke aus Wahrheitstabellen ableiten (siehe auch Kanonform ), wie man eine Wahrheitstabelle als eine Kombination von booleschen Primitiven oder Funktionen ausdrücken kann.

    
vladr 21.03.2009 07:05
quelle
0

Eine andere Lösung für C booleans (ein bisschen schmutzig, aber funktioniert):

((unsigned int)(p) <= (unsigned int)(q))

Es funktioniert, da nach dem C-Standard 0 false darstellt und jeder andere Wert true ( 1 wird von booleschen Operatoren mit int type zurückgegeben).

Die "Schmutzigkeit" besteht darin, dass ich boolesche Werte ( p und q ) als Ganzzahlen verwende, was einigen starken Schreibstrategien (wie MISRA) widerspricht, nun, das ist eine Optimierungsfrage. Du kannst immer #define als Makro verwenden, um die schmutzigen Sachen zu verstecken.

Für die richtige boolesche p und q (mit 0 oder 1 binären Darstellungen) funktioniert es. Andernfalls könnte T->T T nicht erzeugen, wenn p und q willkürliche Werte ungleich Null für die Darstellung von true haben.

Wenn Sie nur das Ergebnis speichern müssen, gibt es seit dem Pentium II die Anweisung cmovcc (Conditional Move) (wie in Deroberts Antwort gezeigt). Für Booleans hatte jedoch selbst der 386 eine Option ohne Verzweigung, die setcc Anweisung, die 0 oder 1 in einer Ergebnisbyteposition (Byte-Register oder Speicher) erzeugt. Sie können dies auch in der Antwort von Derobert sehen, und diese Lösung wird auch zu einem Ergebnis kompiliert, das ein setcc ( setbe : Set, wenn unter oder gleich) enthält.

Die ~p | q -Variante von Derobert und Chris Dolan sollte für die Verarbeitung großer Datenmengen am schnellsten sein, da sie die Implikation für alle Bits von p und q individuell verarbeiten kann.

Beachten Sie, dass nicht einmal die !p || q -Lösung zum Verzweigungscode auf dem x86 kompiliert: Sie verwendet setcc -Anweisungen. Das ist die beste Lösung, wenn p oder q willkürliche Werte ungleich Null enthalten können, die "true" darstellen. Wenn Sie den Typ _Bool verwenden, werden nur wenige Anweisungen generiert.

Ich habe die folgenden Zahlen beim Kompilieren für das x86 erhalten:

%Vor%

Montageergebnis:

%Vor%

Wenn der _Bool -Typ verwendet wird, nutzt der Compiler eindeutig, dass er nur zwei mögliche Werte hat ( 0 für false und 1 für true), was ein sehr ähnliches Ergebnis ergibt wie die ~a | b -Lösung (die einzige Der Unterschied besteht darin, dass letzterer ein Komplement für alle Bits durchführt und nicht nur für das niedrigste Bit.

Das Kompilieren für 64 Bits ergibt ungefähr die gleichen Ergebnisse.

Wie auch immer, es ist klar, dass die Methode keine Rolle spielt, um die Herstellung von Bedingungen zu vermeiden.

    
Jubatian 16.08.2016 11:51
quelle

Tags und Links