Logiczny operator XOR w C ++?

292

Czy jest coś takiego? Po raz pierwszy spotkałem się z praktyczną potrzebą, ale nie widzę takiej w Stroustrup . Zamierzam napisać:

// Detect when exactly one of A,B is equal to five.
return (A==5) ^^ (B==5);

Ale nie ma ^^operatora. Czy mogę użyć bitowej ^tutaj i uzyskać prawidłową odpowiedź (niezależnie od reprezentacji maszynowej prawdy i fałszu)? Nigdy nie mieszam &i &&, lub, |i ||dlatego waham się czynić to z ^i ^^.

bool XOR(bool,bool)Zamiast tego wygodniej byłoby napisać własną funkcję.

RAC
źródło
57
W rzeczywistości Jim, to nie jedyna różnica między & i && na przykład ... 1 && 2 jest Prawdą. ale 1 i 2 => 0. Z tego powodu uważam, że „zwarcie” jest tylko właściwością, którą mają. Logika jest najważniejszą funkcją ...
Brian Postow,
6
Nie wspominając o tym, że 2 i& 3 == prawda, ale 2 i 3 == 2.
David Thornley,
1
David Thomley: Cóż, tak, ale 2 ==> prawda, więc jest ok ... Pamiętaj, że tak naprawdę nie ma żadnych
booleanów
13
@BrianPostow: Tak naprawdę jest w C ++.
Adrian Willenbücher
7
Jak napisano poniżej, oto odpowiedź Dennisa Ritchiego na pytanie, dlaczego nie istnieje: c-faq.com/misc/xor.dmr.html
Tobia

Odpowiedzi:

536

!=Operatora tego celu służy do boolwartości.

Greg Hewgill
źródło
7
Ale fałsz! = Fałsz => fałsz
Jonas,
13
Zauważ, że działa to tylko dla boolean. I ^ działałoby tam doskonale. 2! = 1 => 1, co nie jest tym, czego chcesz! jak mówi LiraNuna, umieszczenie! przed obiema stronami rozwiązuje ten problem. ale znowu możesz użyć
bitów
8
Racja. Ostrożnie wspomniałem o „ boolwartościach”, ponieważ niekoniecznie robi to, co możesz chcieć dla nie-booleanów. A ponieważ jest to C ++, istnieje prawdziwy booltyp zamiast konieczności używania intdo tego celu.
Greg Hewgill,
29
Jeśli chcesz to zrobić po aprostu napisz!(a) != !(a)
Chris Lutz
6
@ChrisLutz: tak, ale uważaj na przeciążonych operatorów.
rwong
246

W przypadku prawdziwej logicznej operacji XOR będzie to działać:

if(!A != !B) {
    // code here
}

Zauważ, że !są tam, aby przekonwertować wartości na wartości logiczne i zanegować je, aby dwie nierówne dodatnie liczby całkowite (każda a true) byłyby ocenione false.

LiraNuna
źródło
6
Nie rozumiem, dlaczego A i B są negowane!
Martin Hansen
26
Głównie do konwersji ich na logiczne. !!działałoby po prostu dobrze zapytać, ale ponieważ muszą być inni, negowanie ich nie szkodzi.
LiraNuna,
4
Pytanie brzmi, czy kompilatory są w stanie odpowiednio to zoptymalizować.
einpoklum
6
Nieświadomość znaczenia normalizacji booli kosztowała mnie 2 dni.
Makan Tayebi
9
@LiraNuna, „dlaczego A i B są negowane!” / „Głównie do konwersji ich na logiczne.” - Myślę, że warto o tym wspomnieć w odpowiedzi.
cp.engr
42

Prawidłowa ręczna logiczna implementacja XOR zależy od tego, jak ściśle chcesz naśladować ogólne zachowanie innych operatorów logicznych ( ||i &&) z twoim XOR. Są dwie ważne rzeczy na temat tych operatorów: 1) gwarantują ocenę zwarcia, 2) wprowadzają punkt sekwencyjny, 3) oceniają operandy tylko raz.

Jak można zrozumieć, oceny XOR nie można zwierać, ponieważ wynik zawsze zależy od obu argumentów. Więc 1 nie wchodzi w rachubę. Ale co z 2? Jeśli nie przejmujesz się 2, to przy znormalizowanych (tj. bool) Wartościach operator !=wykonuje zadanie XOR pod względem wyniku. W razie potrzeby operandy można łatwo znormalizować za pomocą unary !. W ten sposób !A != !Bimplementuje prawidłowy XOR w tym zakresie.

Ale jeśli zależy ci na dodatkowym punkcie sekwencyjnym, ani !=ani bitowe nie ^jest właściwym sposobem na wdrożenie XOR. Jeden z możliwych sposobów prawidłowego wykonania XOR (a, b) może wyglądać następująco

a ? !b : b

Jest to tak blisko, jak to tylko możliwe, aby domowej roboty XOR były „podobne” do ||i &&. Działa to oczywiście tylko wtedy, gdy zaimplementujesz swój XOR jako makro. Funkcja nie zadziała, ponieważ sekwencjonowanie nie będzie miało zastosowania do argumentów funkcji.

Ktoś może jednak powiedzieć, że jedynym powodem mający temperaturę w każdej sekwencji &&i ||jest wspieranie oceny zwarcia, a tym samym XOR nie potrzebuje. To ma sens. Warto jednak wziąć pod uwagę XOR z punktem sekwencyjnym pośrodku. Na przykład następujące wyrażenie

++x > 1 && x < 5

ma zdefiniowane zachowanie i określony wynik w C / C ++ (przynajmniej w odniesieniu do sekwencjonowania). Można więc zasadnie oczekiwać tego samego od logicznego XOR zdefiniowanego przez użytkownika , jak w

XOR(++x > 1, x < 5)

podczas gdy !=oparty na XOR nie ma tej właściwości.

Mrówka
źródło
1
Tracisz drugą ważną rzeczą ||i &&: C) oceniają argumentów w kontekście logicznym. To jest 1 && 2prawda, w przeciwieństwie do tego, 1 & 2która wynosi zero. Podobnie, ^^operator może być przydatny do zapewnienia tej dodatkowej funkcji, oceny operandów w kontekście logicznym. Np. 1 ^^ 2Jest fałszywe (w przeciwieństwie do 1 ^ 2).
Craig McQueen
2
@Craig McQueen: Nie tęsknię za tym. Drugi akapit mojego postu wspomina o tym. Moim zdaniem traktowanie operandów jako wartości boolowskich nie jest krytyczną cechą operatorów logicznych, w tym sensie, że nie zostałyby wprowadzone tylko z tego powodu. Głównym powodem, dla którego zostały wprowadzone, jest ocena zwarciowa i wymagany do tego punkt sekwencji.
AnT
Czy Twoja sugestia nadal działa tylko z makrem? Chociaż prawdą jest, że kolejność parametrów do oceny w funkcji zależy od kompilatora, to czy obecnie nie jest rzadkie różnicowanie od lewej do prawej? Warto również zauważyć w komentarzach, że jeśli wygląda implementacja #define XOR(ll,rr) { ll ? !rr : rr }, to wywołanie podobne int x = 2; XOR(++x > 1, x < 5);da zły wynik. Wezwanie musiałoby zawierać dodatkowe nawiasy, np. W int x = 2; XOR( (++x > 1), (x < 5) );, aby zapewnić poprawny oczekiwany wynik.
RA
1
Ponieważ XOR nie może zwierać, nie jest potrzebny punkt sekwencyjny. Pod tym względem XOR przypomina bardziej +. O ile nie chcesz również argumentować, że (++ x) + x jest równe 2x + 1, punkt sekwencyjny nie jest rozsądny.
hkBst
1
@ hkBst: Myślę, że jest to w pełni uwzględnione w drugiej części mojej odpowiedzi.
AnT
25

Istnieje inny sposób wykonania XOR:

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

Które oczywiście można wykazać poprzez:

#include <iostream>

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

int main()
{
    using namespace std;
    cout << "XOR(true, true):\t" << XOR(true, true) << endl
         << "XOR(true, false):\t" << XOR(true, false) << endl
         << "XOR(false, true):\t" << XOR(false, true) << endl
         << "XOR(false, false):\t" << XOR(false, false) << endl
         << "XOR(0, 0):\t\t" << XOR(0, 0) << endl
         << "XOR(1, 0):\t\t" << XOR(1, 0) << endl
         << "XOR(5, 0):\t\t" << XOR(5, 0) << endl
         << "XOR(20, 0):\t\t" << XOR(20, 0) << endl
         << "XOR(6, 6):\t\t" << XOR(5, 5) << endl
         << "XOR(5, 6):\t\t" << XOR(5, 6) << endl
         << "XOR(1, 1):\t\t" << XOR(1, 1) << endl;
    return 0;
}
Bertie Wheen
źródło
7
Takie podejście może wygenerować dość powolny kod - 3-5x wolniejszy niż (! A)! = (! B) zarówno na clang, jak i gcc przy użyciu libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon
18

Operatora XOR nie można zwierać; tzn. nie można przewidzieć wyniku wyrażenia XOR jedynie poprzez ocenę jego operandu po lewej stronie. Dlatego nie ma powodu, aby podawać ^^wersję.

Mehrdad Afshari
źródło
25
-1, ponieważ główna różnica między && i & to nie tylko zwarcie. 1 i 2 są prawdziwe, ale 1 i 2 są fałszywe. Zwarcie to po prostu przydatny efekt uboczny.
Brian Postow,
6
Odpowiedź nie mówi o &&i &w ogóle. Chodzi o to, że nie ma powodu do wprowadzania ^^. Podejrzewam, że właściwość, która ^^uznałaby każdą wartość inną niż null za 1mało użyteczną. A przynajmniej nie widzę żadnego zastosowania.
Johannes Schaub - litb
6
Również C ++! = Other-languages. W C i C ++, jak pokazano powyżej, zwarcie jest nie tylko miłym dodatkiem, ale ma zasadnicze znaczenie. :)
Johannes Schaub - litb
13
Następnie powinieneś przeczytać odpowiedź Dennisa Ritchiego na pytanie, dlaczego nie istnieje: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb
5
Oto działający link do odpowiedzi Dennisa Ritchiego na pytanie, dlaczego nie istnieje: c-faq.com/misc/xor.dmr.html
Tobia
13

Napisano dobry kod, który rozwiązał problem lepiej niż! A! =! B

Pamiętaj, że musiałem dodać BOOL_DETAIL_OPEN / CLOSE, aby działało na MSVC 2010

/* From: http://groups.google.com/group/comp.std.c++/msg/2ff60fa87e8b6aeb

   Proposed code    left-to-right?  sequence point?  bool args?  bool result?  ICE result?  Singular 'b'?
   --------------   --------------  ---------------  ---------- ------------  -----------  -------------
   a ^ b                  no              no             no          no           yes          yes
   a != b                 no              no             no          no           yes          yes
   (!a)!=(!b)             no              no             no          no           yes          yes
   my_xor_func(a,b)       no              no             yes         yes          no           yes
   a ? !b : b             yes             yes            no          no           yes          no
   a ? !b : !!b           yes             yes            no          no           yes          no
   [* see below]          yes             yes            yes         yes          yes          no
   (( a bool_xor b ))     yes             yes            yes         yes          yes          yes

   [* = a ? !static_cast<bool>(b) : static_cast<bool>(b)]

   But what is this funny "(( a bool_xor b ))"? Well, you can create some
   macros that allow you such a strange syntax. Note that the
   double-brackets are part of the syntax and cannot be removed! The set of
   three macros (plus two internal helper macros) also provides bool_and
   and bool_or. That given, what is it good for? We have && and || already,
   why do we need such a stupid syntax? Well, && and || can't guarantee
   that the arguments are converted to bool and that you get a bool result.
     Think "operator overloads". Here's how the macros look like:

   Note: BOOL_DETAIL_OPEN/CLOSE added to make it work on MSVC 2010
  */

#define BOOL_DETAIL_AND_HELPER(x) static_cast<bool>(x):false
#define BOOL_DETAIL_XOR_HELPER(x) !static_cast<bool>(x):static_cast<bool>(x)

#define BOOL_DETAIL_OPEN (
#define BOOL_DETAIL_CLOSE )

#define bool_and BOOL_DETAIL_CLOSE ? BOOL_DETAIL_AND_HELPER BOOL_DETAIL_OPEN
#define bool_or BOOL_DETAIL_CLOSE ? true:static_cast<bool> BOOL_DETAIL_OPEN
#define bool_xor BOOL_DETAIL_CLOSE ? BOOL_DETAIL_XOR_HELPER BOOL_DETAIL_OPEN
eleganckie kości
źródło
6

Użyj prostego:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
tygi
źródło
5

Oto jak myślę, że piszesz porównanie XOR w C ++:

bool a = true;   // Test by changing to true or false
bool b = false;  // Test by changing to true or false
if (a == !b)     // THIS IS YOUR XOR comparison
{
    // do whatever
}

Dowód

XOR TABLE
 a   b  XOR
--- --- ---
 T   T   F
 T   F   T
 F   T   T
 F   F   F

a == !b TABLE
 a   b  !b  a == !b
--- --- --- -------
 T   T   F     F
 T   F   T     T
 F   T   F     T
 F   F   T     F

Dowód jest taki, że wyczerpujące badanie danych wejściowych i wyjściowych pokazuje, że w dwóch tabelach dla każdego zestawu danych wejściowych wynik jest zawsze identyczny w dwóch tabelach.

Dlatego pierwotne pytanie brzmi: jak pisać:

return (A==5) ^^ (B==5)

Odpowiedź brzmi:

return (A==5) == !(B==5);

Lub jeśli chcesz, napisz

return !(A==5) == (B==5);
Indinfer
źródło
! a! =! b wydaje się być ładniejszy, ponieważ konwertuje twoje argumenty na bool dla ciebie.
xaxxon
3

(A || B) && !(A && B)

Pierwsza część to A OR B, która jest Inclusive OR; druga część to NIE A i B. Razem dostajesz A lub B, ale nie oba A i B.

Zapewni to XOR udowodnione w poniższej tabeli prawdy.

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|
Jedz w Joes
źródło
Nie kopie
xaxxon
Nie ma w tym potrzeby obrony.
xaxxon
1
@xaxxon: Nie widzisz sensu w tych testach !? W StringCreation użyłeś do stworzenia łańcucha, ale w drugim teście nie. Jeśli umieścisz identyczny kod w obu testach i wywołasz różne XOR dla każdego testu (XOR i XOR2), otrzymasz te same wyniki testu. Więc co próbujesz powiedzieć?
SoLaR,
1

Używam „xor” (wygląda na to, że jest słowem kluczowym; przynajmniej w Code :: Blocks robi się pogrubionym), tak jak możesz używać „i” zamiast &&i „lub” zamiast ||.

if (first xor second)...

Tak, to jest bitowe. Przepraszam.

csiz
źródło
2
Domyślam się, że gdzieś są one ukryte. Jestem całkiem pewien, że „i” i „xor” nie są słowami kluczowymi w ansi C ... (przynajmniej nie C79)
Brian Postow,
1
@Brian Postow: Nie wiem, co to jest C79, ale w C ++ 98 andi xorto standardowe makra biblioteczne. Nie pochodzą z „gdzieś”, pochodzą z <iso646.h>. Te makra są również w C99 (nie jestem pewien co do C89 / 90).
AnT
5
@Brian Postow: ... xoroznacza jednak bitowe xor, podczas gdy andjest logiczne i.
AnT
1
Źle wpisałem C89 jako C79 ... i Xor itp. Nie ma w mojej kopii K&R. Nie sądzę, żebym kiedykolwiek używał iso686.h, przynajmniej nie świadomie ... a więc, tak, są one gdzieś
zdefiniowane
2
Z pewnością są w C99, używając tego nagłówka. W C ++ są one zintegrowane z językiem jako „tokeny alternatywne” i można struct A { compl A() { } };na przykład zdefiniować destruktor.
Johannes Schaub - litb
0
#if defined(__OBJC__)
    #define __bool BOOL
    #include <stdbool.h>
    #define __bool bool
#endif

static inline __bool xor(__bool a, __bool b)
{
    return (!a && b) || (a && !b);
}

Działa zgodnie z definicją. Warunkami są wykrywanie, czy używasz Objective-C , który prosi o BOOL zamiast bool (długość jest inna!)

Maxthon Chan
źródło
3
Narusza to zasadę podwójnego podkreślenia.
Tamás Szelei
@ TamásSzelei Niekoniecznie, ponieważ kompilator nie widzi tego, ponieważ jest on wstępnie przetwarzany, aw świecie Objective-C podwójne podkreślenia są dość powszechne.
Maxthon Chan
Dobra uwaga na temat preprocesora, chociaż dla mnie nadal koduje zapach w ten sposób (po co w ogóle używać makra zamiast literówki?). Pytanie nie dotyczyło również celu C.
Tamás Szelei,
@ TamásSzelei Cóż, miałem zwyczaj udostępniania plików nagłówkowych w wielu językach i zwykle wszystkie nagłówki pochodzą z Objective-C. Mój nowy kod nie pachnie teraz zbyt mocno, ale podwójny znak podkreślenia jest nadal używany od czasu do czasu, aby stosować się do nawyków ObjC.
Maxthon Chan,