Operatore logico XOR in C ++?


292

C'è una cosa del genere? È la prima volta che ne ho riscontrato un bisogno pratico, ma non ne vedo uno elencato in Stroustrup . Ho intenzione di scrivere:

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

Ma non c'è nessun ^^operatore. Posso usare bitwise ^qui e ottenere la risposta giusta (indipendentemente dalla rappresentazione automatica di vero e falso)? Non mescolo mai &e &&, o |e ||, quindi esito a farlo con ^e ^^.

bool XOR(bool,bool)Invece mi sentirei più a mio agio nel scrivere la mia funzione.


57
In realtà, Jim, questa non è l'unica differenza tra & e && per esempio ... 1 && 2 è True. ma 1 & 2 => 0. Per questo motivo, penso che il "corto circuito" sia solo una proprietà che hanno. La logica è la caratteristica più importante ...
Brian Postow,

6
Per non parlare del fatto che 2 && 3 == vero, ma 2 & 3 == 2.
David Thornley,

1
David Thomley: Beh, sì, ma 2 ==> vero, quindi va bene ... Ricorda, in realtà non ci sono booleani ...
Brian Postow,

13
@BrianPostow: In realtà, in C ++, ci sono.
Adrian Willenbücher,

7
Come pubblicato di seguito, ecco la risposta di Dennis Ritchie sul perché non esiste: c-faq.com/misc/xor.dmr.html
Tobia

Risposte:


536

L' !=operatore ha questo scopo per i boolvalori.


7
Ma false! = False => false
Jonas,

13
Nota che questo funziona solo per i booleani. E ^ funzionerebbe perfettamente bene lì. 2! = 1 => 1 che non è quello che vuoi! come dice LiraNuna, mettendo un! davanti a entrambi i lati risolve questo problema. ma di nuovo, allora puoi usare bit a bit ^ ...
Brian Postow,

8
Bene, sono stato attento a menzionare "per i boolvalori" perché non fa necessariamente ciò che potresti desiderare per i non booleani. E poiché si tratta di C ++, esiste un booltipo reale invece di doverlo utilizzare inta tale scopo.
Greg Hewgill,

29
Se vuoi farlo per il tipo, ascrivi!(a) != !(a)
Chris Lutz,

6
@ChrisLutz: sì, ma attenzione agli operatori sovraccarichi.
rwong

246

Per una vera operazione XOR logica, questo funzionerà:

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

Nota che !ci sono per convertire i valori in booleani e negarli, in modo che truevalutino due interi positivi disuguali (ciascuno a ) false.


6
Non capisco perché A e B vengano negati!
Martin Hansen,

26
Principalmente per convertirli in booleani. !!funzionerebbe, basta chiedere bene, ma dato che devono essere diversi, negarli non fa male.
LiraNuna,

4
La domanda è: i compilatori sono in grado di ottimizzarlo correttamente.
einpoklum,

6
Non conoscere l'importanza della normalizzazione dei bool mi è costato 2 giorni.
Makan Tayebi,

9
@LiraNuna, "perché A e B sono negati!" / "Principalmente per convertirli in booleani." - Penso che valga la pena menzionarlo nella risposta.
cp.engr

42

La corretta implementazione XOR logica manuale dipende da quanto si desidera imitare il comportamento generale di altri operatori logici ( ||e &&) con il proprio XOR. Ci sono due cose importanti su questi operatori: 1) garantiscono la valutazione del corto circuito, 2) introducono un punto di sequenza, 3) valutano i loro operandi una sola volta.

La valutazione XOR, come capisci, non può essere messa in corto circuito poiché il risultato dipende sempre da entrambi gli operandi. Quindi 1 è fuori discussione. Ma che dire di 2? Se non ti importa di 2, allora con boolvalori normalizzati (cioè ) l'operatore !=fa il lavoro di XOR in termini di risultato. E gli operandi possono essere facilmente normalizzati con unario !, se necessario. In tal modo, !A != !Bimplementa l'XOR corretto in tal senso.

Ma se ti preoccupi del punto di sequenza aggiuntivo, né !=né bit a bit ^è il modo corretto di implementare XOR. Un modo possibile per eseguire correttamente XOR (a, b) potrebbe essere il seguente

a ? !b : b

Questo è in realtà il più vicino possibile a rendere uno XOR fatto in casa "simile" a ||e &&. Questo funzionerà, ovviamente, solo se si implementa XOR come macro. Una funzione non funziona, poiché il sequenziamento non si applica agli argomenti della funzione.

Qualcuno potrebbe dire, però, che l'unica ragione di avere un punto di sequenza a ciascun &&ed ||è per supportare la valutazione corto-circuito, e quindi XOR non ne ha bisogno. Questo ha senso, in realtà. Tuttavia, vale la pena considerare di avere un XOR con un punto sequenza nel mezzo. Ad esempio, la seguente espressione

++x > 1 && x < 5

ha un comportamento definito e risultati specifici in C / C ++ (almeno per quanto riguarda il sequenziamento). Quindi, ci si potrebbe ragionevolmente aspettare lo stesso dallo XOR logico definito dall'utente , come in

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

mentre un !=XOR a base non ha questa proprietà.


1
Ti manca l'altra cosa importante ||e &&: C) valutano gli operandi in un contesto booleano. Cioè, 1 && 2è vero, a differenza di 1 & 2zero. Allo stesso modo, un ^^operatore potrebbe essere utile per fornire questa funzionalità aggiuntiva, per valutare gli operandi in un contesto booleano. Ad esempio 1 ^^ 2è falso (a differenza 1 ^ 2).
Craig McQueen,

2
@Craig McQueen: non mi manca. Il secondo paragrafo del mio post lo menziona. A mio avviso, trattare gli operandi come valori booleani non è una caratteristica critica degli operatori logici, nel senso che non sarebbero stati introdotti solo per quel motivo. Il motivo principale per cui sono stati introdotti è la valutazione in cortocircuito e il punto di sequenza richiesto per questo.
AnT

Al giorno d'oggi, il tuo suggerimento funzionerebbe ancora solo con una macro? Anche se è vero che l'ordine dei parametri da valutare in una funzione dipende dal compilatore, non è attualmente raro differire da sinistra a destra? Inoltre, potrebbe valere la pena di notare qui nei commenti che se un'implementazione appare #define XOR(ll,rr) { ll ? !rr : rr }, allora una chiamata come int x = 2; XOR(++x > 1, x < 5);darà il risultato sbagliato. La chiamata dovrebbe avere parentesi extra, come in int x = 2; XOR( (++x > 1), (x < 5) );, per dare il risultato previsto corretto.
RA

1
Poiché XOR non può cortocircuitare, non è necessario un punto sequenza. XOR è più simile a + in questo senso. A meno che non si desideri anche argomentare che (++ x) + x sia uguale a 2x + 1, il punto di sequenza non è ragionevole.
hk

1
@hkBst: penso che questo sia completamente coperto nella seconda parte della mia risposta.
AnT

25

C'è un altro modo di fare XOR:

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

Che ovviamente può essere dimostrato di funzionare tramite:

#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;
}

7
Questo approccio può generare un codice piuttosto lento - 3-5 volte più lento di (! A)! = (! B) sia su clang che su gcc usando libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon

18

L'operatore XOR non può essere cortocircuitato; cioè non è possibile prevedere il risultato di un'espressione XOR semplicemente valutando il suo operando di sinistra. Pertanto, non c'è motivo di fornire una ^^versione.


25
-1 perché la differenza principale tra && e & non è solo il corto circuito. 1 && 2 è vero, ma 1 & 2 è falso. Il corto circuito è solo un utile effetto collaterale.
Brian Postow,

6
La risposta non sta parlando &&e &a tutti. Il punto è che non c'è motivo di presentarsi ^^. La proprietà che ^^considererebbe qualsiasi valore non nullo in quanto 1non è veramente utile, sospetto. O almeno non riesco a vedere alcun uso.
Johannes Schaub - litb

6
Anche C ++! = Altre lingue. In C e C ++, come mostrato sopra, il cortocircuito non è solo una cosa piacevole da avere, ma è fondamentalmente importante. :)
Johannes Schaub - litb

13
Quindi dovresti leggere la risposta di Dennis Ritchie al perché non esiste: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb

5
Ecco un link funzionante sulla risposta di Dennis Ritchie sul perché non esiste: c-faq.com/misc/xor.dmr.html
Tobia

13

È stato pubblicato un buon codice che ha risolto il problema meglio di! A! =! B

Nota che ho dovuto aggiungere BOOL_DETAIL_OPEN / CLOSE in modo che funzionasse su 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

6

Usa un semplice:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));

5

Ecco come penso che tu scriva un confronto XOR in 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
}

Prova

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

La prova è che uno studio esaustivo di input e output mostra che nelle due tabelle, per ogni set di input il risultato è sempre identico nelle due tabelle.

Pertanto, la domanda originale è come scrivere:

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

La risposta sarebbe

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

O se ti piace, scrivi

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

! a! =! b sembra essere più bello perché converte i tuoi argomenti in bool per te.
xaxxon,

3

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

La prima parte è A OR B, ovvero OR inclusivo; la seconda parte è, NON A E B. Insieme ottieni A o B, ma non entrambi A e B.

Ciò fornirà lo XOR dimostrato nella tabella della verità di seguito.

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|

Non sto scavando le prestazioni su questo approccio: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon

Non è necessario essere difensivi al riguardo.
xaxxon,

1
@xaxxon: non vedo il punto in questo benchmark !? In StringCreation hai usato nella creazione di string, ma nel secondo benchmark no. Se si inserisce un codice identico in entrambi i benchmark e si chiamano XOR diversi per ciascun benchmark (XOR e XOR2) si ottengono gli stessi risultati del benchmark. Quindi cosa hai cercato di dire?
SoLaR,

1

Uso "xor" (sembra che sia una parola chiave; in Code :: Blocks almeno diventa grassetto) così come puoi usare "e" invece di &&e "o" invece di ||.

if (first xor second)...

Sì, è bit a bit. Scusate.


2
Immagino che quelli siano nascosti #define da qualche parte. Sono abbastanza sicuro che "e" e "xor" non sono parole chiave in ansi C ... (almeno non C79)
Brian Postow,

1
@Brian Postow: non so cosa sia C79, ma in C ++ 98 ande xorsono macro di libreria standard. Non provengono da "da qualche parte", sono da <iso646.h>. Queste macro sono anche in C99 (non sono sicuro di C89 / 90).
AnT

5
@Brian Postow: ... xorsta per xor bit a bit , mentre andè logico e.
AnT

1
Ho sbagliato a scrivere C89 come C79 ... ee xor etc non sono nella mia copia di K&R. Non credo di aver mai usato iso686.h, almeno non consapevolmente ... e quindi sì, sono #define da qualche parte, ti capita di sapere dove si trova quel B-)
Brian Postow,

2
Sono certamente in C99 usando quell'intestazione. In C ++, sono integrati nel linguaggio come "token alternativi" e struct A { compl A() { } };, ad esempio , puoi fare per definire un distruttore.
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);
}

Funziona come definito. I condizionali sono per rilevare se stai usando Objective-C , che richiede BOOL invece di bool (la lunghezza è diversa!)


3
Ciò viola la doppia regola di sottolineatura.
Tamás Szelei,

@ TamásSzelei Non necessariamente perché il compilatore non lo vede come è preelaborato e nel mondo Objective-C i doppi trattini bassi sono abbastanza comuni.
Maxthon Chan,

Un buon punto per il preprocessore, anche se per me è ancora odore di codice in questo modo (perché usare comunque una macro anziché un typedef?). Inoltre, la domanda non riguardava Objective-C.
Tamás Szelei,

@ TamásSzelei Beh, avevo l'abitudine di condividere file di intestazione in più lingue, e di solito tutte le intestazioni provengono da Objective-C. Il mio nuovo codice non ha un odore eccessivo ora, ma il doppio trattino di sottolineatura viene ancora usato di tanto in tanto per aderire alle abitudini di ObjC.
Maxthon Chan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.