Operator XOR logis dalam C ++?


292

Apakah ada hal seperti itu? Ini adalah pertama kalinya saya menemui kebutuhan praktis untuk itu, tetapi saya tidak melihat satu pun yang terdaftar di Stroustrup . Saya bermaksud menulis:

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

Tetapi tidak ada ^^operator. Bisakah saya menggunakan bitwise di ^sini dan mendapatkan jawaban yang benar (terlepas dari representasi mesin yang benar dan salah)? Saya tidak pernah bergaul &dan &&, atau |dan ||, jadi saya ragu untuk melakukannya dengan ^dan ^^.

Saya akan lebih nyaman menulis bool XOR(bool,bool)fungsi saya sendiri .


57
Sebenarnya, Jim, itu bukan satu-satunya perbedaan antara & dan && misalnya ... 1 && 2 Benar. tetapi 1 & 2 => 0. Karena itu, saya berpikir bahwa "korsleting" hanyalah properti yang kebetulan mereka miliki. Logis adalah fitur yang lebih penting ...
Brian Postow

6
Belum lagi bahwa 2 && 3 == benar, tetapi 2 & 3 == 2.
David Thornley

1
David Thomley: Ya, tapi 2 == benar, jadi tidak apa-apa ... Ingat, benar-benar tidak ada boolean ...
Brian Postow

13
@BrianPostow: Sebenarnya, di C ++, ada.
Adrian Willenbücher

7
Seperti yang diposting di bawah ini, inilah jawaban Dennis Ritchie mengapa tidak ada: c-faq.com/misc/xor.dmr.html
Tobia

Jawaban:


536

The !=Operator melayani tujuan ini untuk boolnilai-nilai.


7
Tapi salah! = Salah => salah
Jonas

13
Perhatikan bahwa ini hanya berfungsi untuk boolean. Dan ^ akan bekerja dengan baik di sana. 2! = 1 => 1 yang bukan yang Anda inginkan! seperti yang dikatakan LiraNuna, menempatkan! Infront dari kedua belah pihak memecahkan masalah itu. tapi sekali lagi, maka Anda dapat menggunakan bitwise ^ ...
Brian Postow

8
Benar, saya berhati-hati untuk menyebutkan "untuk boolnilai" karena tidak selalu melakukan apa yang Anda inginkan untuk non-boolean. Dan karena ini adalah C ++, ada booltipe nyata daripada harus digunakan intuntuk tujuan itu.
Greg Hewgill

29
Jika Anda ingin melakukannya untuk tipe atulis saja!(a) != !(a)
Chris Lutz

6
@ ChrisLutz: ya, tapi waspadalah terhadap operator yang kelebihan beban.
rwong

246

Untuk operasi XOR logis sejati, ini akan bekerja:

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

Perhatikan !apakah ada untuk mengkonversi nilai menjadi boolean dan meniadakannya, sehingga dua bilangan bulat positif yang tidak sama (masing-masing a true) akan dievaluasi false.


6
Saya tidak mengerti mengapa A dan B dinegasikan dengan!
Martin Hansen

26
Terutama untuk mengubahnya menjadi boolean. !!akan bekerja hanya bertanya dengan baik, tetapi karena mereka harus berbeda, meniadakan mereka tidak ada salahnya.
LiraNuna

4
Pertanyaannya adalah, apakah kompiler dapat mengoptimalkan ini dengan benar.
einpoklum

6
Tidak tahu pentingnya menormalkan bools menghabiskan waktu 2 hari.
Makan Tayebi

9
@LiraNuna, "mengapa A dan B dinegasikan dengan!" / "Terutama untuk mengubahnya menjadi boolean." - Saya pikir ini layak disebutkan dalam jawabannya.
cp.engr

42

Implementasi XOR logis manual yang tepat tergantung pada seberapa dekat Anda ingin meniru perilaku umum operator logis lainnya ( ||dan &&) dengan XOR Anda. Ada dua hal penting tentang operator ini: 1) mereka menjamin evaluasi hubung singkat, 2) mereka memperkenalkan titik urutan, 3) mereka mengevaluasi operan mereka hanya sekali.

Evaluasi XOR, seperti yang Anda mengerti, tidak dapat dihubung pendek karena hasilnya selalu tergantung pada kedua operan. Jadi saya tidak mungkin. Tapi bagaimana dengan 2? Jika Anda tidak peduli dengan 2, maka dengan booloperator nilai yang dinormalisasi (yaitu ) !=melakukan pekerjaan XOR dalam hal hasil. Dan operan dapat dengan mudah dinormalisasi dengan unary !, jika perlu. Dengan demikian !A != !Bmengimplementasikan XOR yang tepat dalam hal itu.

Tetapi jika Anda peduli dengan titik sekuens tambahan, baik !=bitwise atau tidak ^adalah cara yang tepat untuk menerapkan XOR. Salah satu cara yang mungkin untuk melakukan XOR (a, b) dengan benar mungkin terlihat sebagai berikut

a ? !b : b

Ini sebenarnya sedekat yang Anda bisa untuk membuat XOR buatan sendiri "mirip" dengan ||dan &&. Ini hanya akan berfungsi, tentu saja, jika Anda menerapkan XOR Anda sebagai makro. Fungsi tidak akan berfungsi, karena pengurutan tidak akan berlaku untuk argumen fungsi.

Seseorang mungkin mengatakan, bahwa satu-satunya alasan memiliki titik urutan pada masing &&- masing dan ||untuk mendukung evaluasi hubung singkat, dan dengan demikian XOR tidak memerlukannya. Ini masuk akal, sebenarnya. Namun, ada baiknya mempertimbangkan memiliki XOR dengan titik urutan di tengah. Misalnya, ungkapan berikut

++x > 1 && x < 5

telah mendefinisikan perilaku dan hasil khusus dalam C / C ++ (berkaitan dengan sekuensing setidaknya). Jadi, orang mungkin mengharapkan hal yang sama dari XOR logis yang ditentukan pengguna , seperti pada

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

sementara !=XOR yang berbasis tidak memiliki properti ini.


1
Anda melewatkan hal penting lainnya tentang ||dan &&: C) mereka mengevaluasi operan dalam konteks boolean. Artinya, 1 && 2benar, tidak seperti 1 & 2yang nol. Demikian juga, ^^operator dapat berguna untuk menyediakan fitur tambahan ini, untuk mengevaluasi operan dalam konteks boolean. Misalnya 1 ^^ 2salah (tidak seperti 1 ^ 2).
Craig McQueen

2
@Craig McQueen: Saya tidak melewatkannya. Paragraf kedua dari posting saya menyebutkannya. Menurut pendapat saya, memperlakukan operan sebagai nilai boolean bukan fitur kritis dari operator logis, dalam arti bahwa mereka tidak akan diperkenalkan karena alasan itu saja. Alasan utama mereka diperkenalkan adalah evaluasi hubungan pendek dan titik urutan diperlukan untuk itu.
AnT

Saat ini, apakah saran Anda masih bekerja dengan makro? Meskipun benar bahwa urutan parameter yang akan dievaluasi dalam suatu fungsi bergantung pada compiler, bukankah saat ini jarang berbeda dari kiri ke kanan? Juga, mungkin perlu dicatat di sini di komentar bahwa jika implementasi terlihat seperti #define XOR(ll,rr) { ll ? !rr : rr }, maka panggilan seperti int x = 2; XOR(++x > 1, x < 5);akan memberikan hasil yang salah. Panggilan itu harus memiliki tanda kurung tambahan, seperti di int x = 2; XOR( (++x > 1), (x < 5) );, untuk memberikan hasil yang diharapkan benar.
RA

1
Karena XOR tidak dapat mengalami hubungan pendek, tidak perlu untuk titik-urutan. XOR lebih seperti + dalam hal itu. Kecuali jika Anda juga ingin berdebat untuk (++ x) + x sama dengan 2x + 1, titik urutannya tidak masuk akal.
hkBst

1
@ hkBst: Saya pikir ini sepenuhnya dibahas pada bagian kedua dari jawaban saya.
AnT

25

Ada cara lain untuk melakukan XOR:

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

Yang jelas dapat ditunjukkan bekerja melalui:

#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
Pendekatan ini dapat menghasilkan beberapa kode yang sangat lambat - 3-5x lebih lambat daripada (! A)! = (! B) pada dentang dan gcc menggunakan libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon

18

Operator XOR tidak dapat dihubung pendek; yaitu Anda tidak dapat memprediksi hasil ekspresi XOR hanya dengan mengevaluasi operan tangan kirinya. Jadi, tidak ada alasan untuk menyediakan ^^versi.


25
-1 karena perbedaan utama antara && dan & bukan hanya hubungan arus pendek. 1 && 2 Benar, tetapi 1 & 2 salah. Hubungan arus pendek hanyalah efek samping yang berguna.
Brian Postow

6
Jawabannya tidak membicarakan &&dan &sama sekali. Maksudnya adalah tidak ada alasan untuk memperkenalkan ^^. Properti yang ^^akan menganggap nilai bukan nol sebagai 1tidak benar-benar berguna, saya curiga. Atau setidaknya saya tidak bisa melihat kegunaannya.
Johannes Schaub - litb

6
Juga C ++! = Bahasa lainnya. Dalam C dan C ++ seperti yang ditunjukkan di atas hubung singkat bukan hanya beberapa hal yang menyenangkan tetapi sangat penting. :)
Johannes Schaub - litb

13
Maka Anda harus membaca jawaban Dennis Ritchie mengapa tidak ada: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb

5
Berikut ini tautan yang berfungsi pada jawaban Dennis Ritchie mengapa tidak ada: c-faq.com/misc/xor.dmr.html
Tobia

13

Ada beberapa kode yang diposting yang memecahkan masalah lebih baik daripada! A! =! B

Perhatikan bahwa saya harus menambahkan BOOL_DETAIL_OPEN / CLOSE agar ini akan berfungsi pada 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

Gunakan yang sederhana:

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

5

Berikut adalah bagaimana saya pikir Anda menulis perbandingan XOR di 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
}

Bukti

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

Buktinya adalah bahwa studi lengkap input dan output menunjukkan bahwa dalam dua tabel, untuk setiap set input hasilnya selalu sama dalam dua tabel.

Karena itu, pertanyaan awal adalah bagaimana cara menulis:

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

Jawabannya adalah

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

Atau jika Anda suka, tulis

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

! a! =! b tampaknya lebih baik karena mengubah argumen Anda menjadi bool untuk Anda.
xaxxon

3

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

Bagian pertama adalah A OR B, yang merupakan OR Inklusif; bagian kedua adalah, BUKAN A DAN B. Bersama-sama Anda mendapatkan A atau B, tetapi tidak A dan B.

Ini akan memberikan XOR terbukti dalam tabel kebenaran di bawah ini.

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

Saya tidak menggali kinerja pada pendekatan ini: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon

Tidak perlu bersikap defensif tentang hal itu.
xaxxon

1
@xaxxon: Tidak melihat maksud dari tolok ukur ini !? Di StringCreation Anda digunakan dalam pembuatan string, tetapi di benchmark kedua Anda tidak. Jika Anda menempatkan kode identik di kedua tolok ukur, dan memanggil XOR berbeda untuk setiap tolok ukur (XOR, dan XOR2), Anda mendapatkan hasil tolok ukur yang sama. Jadi apa yang ingin Anda katakan?
SoLaR

1

Saya menggunakan "xor" (sepertinya itu adalah kata kunci; dalam Kode :: Blok setidaknya menjadi berani) sama seperti Anda dapat menggunakan "dan" bukannya &&dan "atau" bukannya ||.

if (first xor second)...

Ya, itu bitwise. Maaf.


2
Saya menduga itu adalah #define tersembunyi dari suatu tempat. Saya cukup yakin "dan" dan "xor" bukan kata kunci dalam ansi C ... (setidaknya bukan C79)
Brian Postow

1
@ Brian Postow: Saya tidak tahu apa itu C79, tetapi di C ++ 98 anddan xormerupakan macro library standar. Mereka bukan dari "suatu tempat", mereka berasal dari <iso646.h>. Makro ini juga ada di C99 (tidak yakin tentang C89 / 90).
AnT

5
@ Brian Postow: ... xorsingkatan dari bitwise xor though, sementara andmasih logis dan.
AnT

1
Saya salah ketik C89 sebagai C79 ... dan dan xor dll tidak ada dalam salinan K&R saya. Saya tidak berpikir saya pernah menggunakan iso686.h, setidaknya tidak secara sadar ... dan jadi, ya, mereka adalah #define dari suatu tempat, Anda kebetulan tahu di mana tempat itu adalah B-)
Brian Postow

2
Mereka pasti di C99 menggunakan header itu. Di C ++, mereka diintegrasikan ke dalam bahasa sebagai "token alternatif", dan Anda dapat melakukannya struct A { compl A() { } };untuk mendefinisikan destruktor, misalnya.
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);
}

Ini berfungsi seperti yang didefinisikan. Persyaratannya adalah untuk mendeteksi jika Anda menggunakan Objective-C , yang meminta BOOL alih-alih bool (panjangnya berbeda!)


3
Ini melanggar aturan garis bawah ganda.
Tamás Szelei

@ TamásSzelei Tidak harus karena kompiler tidak melihat bahwa ketika ia diproses sebelumnya, dan di dunia Objective-C, garis bawah dua kali cukup umum.
Maxthon Chan

Poin bagus tentang preprocessor, meskipun bagi saya itu masih kode bau dengan cara ini (mengapa tetap menggunakan makro, bukan typedef?). Juga, pertanyaannya bukan tentang Objective-C.
Tamás Szelei

@ TamásSzelei Yah saya dulu punya kebiasaan berbagi file header di berbagai bahasa, dan biasanya semua header berasal dari Objective-C. Kode baru saya tidak berbau terlalu banyak sekarang, tetapi garis bawah ganda masih digunakan dari waktu ke waktu untuk mematuhi kebiasaan ObjC.
Maxthon Chan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.