Toán tử XOR logic trong C ++?


292

Có một điều như vậy? Đây là lần đầu tiên tôi gặp phải nhu cầu thiết thực cho nó, nhưng tôi không thấy một danh sách nào được liệt kê trong Stroustrup . Tôi dự định viết:

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

Nhưng không có ^^nhà điều hành. Tôi có thể sử dụng bitwise ^ở đây và nhận được câu trả lời đúng (bất kể đại diện của máy là đúng và sai) không? Tôi không bao giờ trộn lẫn &, và &&, |||vì vậy, tôi ngần ngại làm điều đó với ^^^.

bool XOR(bool,bool)Thay vào đó tôi sẽ thoải mái hơn khi viết chức năng của riêng mình .


57
Trên thực tế, Jim, đó không phải là sự khác biệt duy nhất giữa & và && ví dụ ... 1 && 2 là Đúng. nhưng 1 & 2 => 0. Do đó, tôi nghĩ rằng "đoản mạch" chỉ là một tài sản mà họ tình cờ có. Hợp lý là tính năng quan trọng hơn ...
Brian Postow

6
Chưa kể rằng 2 && 3 == đúng, nhưng 2 & 3 == 2.
David Thornley

1
David Thomley: Vâng, vâng, nhưng 2 ==> đúng, vậy là ổn ... Hãy nhớ rằng, thực sự không có bất kỳ
booleans

13
@BrianPostow: Thật ra, trong C ++, có.
Adrian Willenbücher

7
Như được đăng dưới đây, đây là câu trả lời của Dennis Ritchie về lý do tại sao nó không tồn tại: c-faq.com/misc/xor.dmr.html
Tobia

Câu trả lời:


536

Các !=nhà điều hành phục vụ mục đích này cho boolcác giá trị.


7
Nhưng sai! = Sai => sai
Jonas

13
Lưu ý rằng điều này chỉ hoạt động cho booleans. Và ^ sẽ làm việc hoàn toàn tốt ở đó. 2! = 1 => 1 không phải là điều bạn muốn! như LiraNuna nói, đặt a! trước mặt cả hai bên giải quyết vấn đề đó. nhưng một lần nữa, sau đó bạn có thể sử dụng bitwise ^ ...
Brian Postow

8
Đúng vậy, tôi đã cẩn thận đề cập đến "cho boolcác giá trị" bởi vì nó không nhất thiết phải làm những gì bạn có thể muốn cho những người không phải là booleans. Và vì đây là C ++, tồn tại một boolloại thực sự thay vì phải sử dụng intcho mục đích đó.
Greg Hewgill

29
Nếu bạn muốn làm điều đó cho loại achỉ cần viết!(a) != !(a)
Chris Lutz

6
@ChrisLutz: có, nhưng hãy cẩn thận với các nhà khai thác quá tải.
rwong 11/03/2015

246

Đối với một hoạt động XOR logic thực sự, điều này sẽ hoạt động:

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

Lưu ý rằng !có để chuyển đổi các giá trị thành booleans và phủ định chúng, sao cho hai số nguyên dương không bằng nhau (mỗi a true) sẽ đánh giá thành false.


6
Tôi không hiểu tại sao A và B bị từ chối!
Martin Hansen

26
Chủ yếu để chuyển đổi chúng sang boolean. !!sẽ làm việc chỉ cần hỏi tốt, nhưng vì họ cần phải khác biệt, phủ nhận chúng không có hại.
LiraNuna

4
Câu hỏi là, trình biên dịch có thể tối ưu hóa đúng điều này.
einpoklum

6
Không biết tầm quan trọng của việc bình thường hóa bool tốn 2 ngày.
Makan Tayebi

9
@LiraNuna, "tại sao A và B bị từ chối!" / "Chủ yếu để chuyển đổi chúng thành boolean." - Tôi nghĩ rằng điều này đáng được đề cập trong câu trả lời.
cp.engr

42

Việc triển khai XOR logic thủ công đúng cách phụ thuộc vào mức độ bạn muốn bắt chước hành vi chung của các toán tử logic khác ( ||&&) với XOR của bạn. Có hai điều quan trọng về các toán tử này: 1) chúng đảm bảo đánh giá ngắn mạch, 2) chúng giới thiệu một điểm thứ tự, 3) chúng chỉ đánh giá toán hạng của chúng một lần.

Đánh giá XOR, như bạn hiểu, không thể được ngắn mạch vì kết quả luôn phụ thuộc vào cả hai toán hạng. Vì vậy, 1 là ra khỏi câu hỏi. Nhưng còn 2 thì sao? Nếu bạn không quan tâm đến 2, thì với booltoán tử giá trị chuẩn hóa (nghĩa là ) !=thực hiện công việc của XOR về mặt kết quả. Và các toán hạng có thể dễ dàng được chuẩn hóa với unary !, nếu cần thiết. Do đó !A != !Bthực hiện XOR thích hợp trong vấn đề đó.

Nhưng nếu bạn quan tâm đến điểm chuỗi bổ sung, thì cũng không !=phải ^là cách thích hợp để thực hiện XOR. Một cách có thể để làm XOR (a, b) chính xác có thể trông như sau

a ? !b : b

Điều này thực sự gần đến mức bạn có thể tạo ra một XOR tự chế "tương tự" ||&&. Tất nhiên, điều này sẽ chỉ hoạt động nếu bạn triển khai XOR dưới dạng macro. Một hàm sẽ không được thực hiện, vì trình tự sẽ không áp dụng cho các đối số của hàm.

Tuy nhiên, ai đó có thể nói rằng lý do duy nhất để có một điểm thứ tự ở mỗi điểm &&||là để hỗ trợ đánh giá ngắn mạch, và do đó XOR không cần một điểm. Điều này có ý nghĩa, thực sự. Tuy nhiên, đáng để xem xét có một XOR với một điểm thứ tự ở giữa. Ví dụ: biểu thức sau

++x > 1 && x < 5

đã xác định hành vi và kết quả cụ thể trong C / C ++ (ít nhất là liên quan đến giải trình tự). Vì vậy, người ta có thể mong đợi một cách hợp lý điều tương tự từ XOR logic do người dùng định nghĩa , như trong

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

trong khi !=XOR dựa trên cơ sở không có thuộc tính này.


1
Bạn đang thiếu điều quan trọng khác về ||&&: C) họ đánh giá các toán hạng trong ngữ cảnh boolean. Đó là, 1 && 2là sự thật, không giống như 1 & 2không. Tương tự, một ^^toán tử có thể hữu ích cho việc cung cấp tính năng bổ sung này, để đánh giá các toán hạng trong ngữ cảnh boolean. Ví dụ 1 ^^ 2là sai (không giống như 1 ^ 2).
Craig McQueen

2
@Craig McQueen: Tôi không thiếu nó. Đoạn thứ hai của bài viết của tôi đề cập đến nó. Theo tôi, coi toán hạng là giá trị boolean không phải là một tính năng quan trọng của toán tử logic, theo nghĩa là chúng sẽ không được giới thiệu chỉ vì lý do đó. Lý do chính mà họ được giới thiệu là đánh giá ngắn mạch và điểm trình tự cần thiết cho điều đó.
AnT

Ngày nay, đề xuất của bạn vẫn chỉ hoạt động với một macro? Mặc dù đúng là thứ tự của các tham số được đánh giá trong một hàm phụ thuộc vào trình biên dịch, nhưng hiện tại nó có hiếm khi khác biệt từ trái sang phải không? Ngoài ra, có thể đáng lưu ý ở đây trong các ý kiến ​​rằng nếu việc triển khai trông như thế nào #define XOR(ll,rr) { ll ? !rr : rr }, thì một cuộc gọi như thế int x = 2; XOR(++x > 1, x < 5);sẽ cho kết quả sai. Cuộc gọi sẽ phải có thêm dấu ngoặc đơn, như trong int x = 2; XOR( (++x > 1), (x < 5) );, để đưa ra kết quả mong đợi chính xác.
RA

1
Vì XOR không thể đoản mạch, nên không cần điểm nối tiếp. XOR giống + hơn về vấn đề đó. Trừ khi bạn cũng muốn tranh luận về (++ x) + x bằng 2x + 1, điểm chuỗi không hợp lý.
hkBst

1
@hkBst: Tôi nghĩ rằng điều này được đề cập đầy đủ trong phần thứ hai của câu trả lời của tôi.
AnT

25

Có một cách khác để làm XOR:

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

Mà rõ ràng có thể được chứng minh để làm việc thông qua:

#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
Cách tiếp cận này có thể tạo ra một số mã khá chậm - chậm hơn 3-5 lần so với (! A)! = (! B) trên cả clang và gcc bằng libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon

18

Toán tử XOR không thể được ngắn mạch; tức là bạn không thể dự đoán kết quả của biểu thức XOR chỉ bằng cách đánh giá toán hạng tay trái của nó. Vì vậy, không có lý do để cung cấp một ^^phiên bản.


25
-1 vì sự khác biệt chính giữa && và & không chỉ là ngắn mạch. 1 && 2 là Đúng, nhưng 1 & 2 là sai. Đoản mạch chỉ là một tác dụng phụ tiện dụng.
Brian Postow

6
Câu trả lời là không nói về &&&ở tất cả. Quan điểm của nó là không có lý do để giới thiệu ^^. Tài sản ^^sẽ coi bất kỳ giá trị khác 1không là không thực sự hữu ích, tôi nghi ngờ. Hoặc ít nhất tôi không thể thấy bất kỳ việc sử dụng.
Julian Schaub - litb

6
Ngoài ra C ++! = Ngôn ngữ khác. Trong C và C ++ như được trình bày ở trên, ngắn mạch không chỉ là một số thứ tốt đẹp mà về cơ bản nó rất quan trọng. :)
Johannes Schaub - litb

13
Sau đó, bạn nên đọc câu trả lời của Dennis Ritchie về lý do tại sao nó không tồn tại: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb

5
Đây là một liên kết hoạt động trên câu trả lời của Dennis Ritchie về lý do tại sao nó không tồn tại: c-faq.com/misc/xor.dmr.html
Tobia

13

Có một số mã tốt được đăng đã giải quyết vấn đề tốt hơn! A! =! B

Lưu ý rằng tôi phải thêm BOOL_DETAIL_OPEN / ĐÓNG để nó hoạt động trên 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

Sử dụng đơn giản:

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

5

Đây là cách tôi nghĩ bạn viết một so sánh XOR trong 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
}

Bằng chứng

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

Bằng chứng là một nghiên cứu đầy đủ về đầu vào và đầu ra cho thấy rằng trong hai bảng, với mỗi bộ đầu vào, kết quả luôn giống nhau trong hai bảng.

Do đó, câu hỏi ban đầu là làm thế nào để viết:

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

Câu trả lời sẽ là

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

Hoặc nếu bạn thích, viết

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

! a! =! b dường như đẹp hơn bởi vì nó chuyển đổi các đối số của bạn thành bool cho bạn.
xaxxon

3

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

Phần đầu tiên là A HOẶC B, là Bao gồm HOẶC; phần thứ hai là, KHÔNG phải A VÀ B. Cùng nhau bạn có được A hoặc B, nhưng không phải cả A và B.

Điều này sẽ cung cấp XOR đã được chứng minh trong bảng sự thật dưới đây.

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

Tôi không đào hiệu suất theo cách tiếp cận này: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon

Không cần phải phòng thủ về nó.
xaxxon

1
@xaxxon: Đừng nhìn thấy điểm trong điểm chuẩn này!? Trong StringCreation, bạn đã sử dụng trong việc tạo chuỗi, nhưng trong điểm chuẩn thứ hai thì không. Nếu bạn đặt mã giống hệt nhau trong cả hai điểm chuẩn và gọi các XOR khác nhau cho mỗi điểm chuẩn (XOR và XOR2), bạn sẽ nhận được kết quả điểm chuẩn giống nhau. Vì vậy, những gì bạn đã cố gắng để nói?
SoLaR

1

Tôi sử dụng "xor" (có vẻ như đó là một từ khóa; trong Code :: Blocks ít nhất nó được in đậm) giống như bạn có thể sử dụng "và" thay vì &&và "hoặc" thay vì ||.

if (first xor second)...

Vâng, nó là bitwise. Lấy làm tiếc.


2
Tôi đoán rằng những thứ đó được ẩn #defines từ đâu đó. Tôi khá chắc chắn "và" và "xor" không phải là từ khóa trong ansi C ... (ít nhất không phải là C79)
Brian Postow

1
@Brian Postow: Tôi không biết C79 là gì, nhưng trong C ++ 98 andxorlà các macro thư viện chuẩn. Thay không đến từ "một nơi nào đó", họ đến từ <iso646.h>. Các macro này cũng có trong C99 (không chắc chắn về C89 / 90).
AnT

5
@Brian Postow: ... xorlà viết tắt của bitwise xor, trong khi andlogic và.
AnT

1
Tôi đã nhầm C89 là C79 ... và xor v.v. không có trong bản sao K & R của tôi. Tôi không nghĩ rằng tôi đã từng sử dụng iso686.h, ít nhất là không cố ý ... và vì vậy, vâng, chúng là #defines từ một nơi nào đó, bạn
tình cờ

2
Họ chắc chắn trong C99 sử dụng tiêu đề đó. Trong C ++, chúng được tích hợp vào ngôn ngữ dưới dạng "mã thông báo thay thế" và bạn có thể thực hiện struct A { compl A() { } };để xác định hàm hủy.
Julian 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);
}

Nó hoạt động như được định nghĩa. Các điều kiện là để phát hiện nếu bạn đang sử dụng Objective-C , yêu cầu BOOL thay vì bool (độ dài là khác nhau!)


3
Điều này vi phạm quy tắc gạch dưới kép.
Tamás Szelei

@ TamásSzelei Không nhất thiết là trình biên dịch không thấy điều đó vì nó được xử lý trước và trong thế giới hai mặt của Objective-C là khá phổ biến.
Maxthon Chan

Điểm hay về bộ tiền xử lý, mặc dù với tôi nó vẫn có mùi mã theo cách này (tại sao lại sử dụng macro thay vì typedef?). Ngoài ra, câu hỏi không phải là về Mục tiêu-C.
Tamás Szelei

@ TamásSzelei Vâng, tôi thường có thói quen chia sẻ các tệp tiêu đề trên nhiều ngôn ngữ và thông thường tất cả các tiêu đề đều đến từ Objective-C. Mã mới của tôi bây giờ không có mùi quá nhiều, nhưng dấu gạch dưới đôi vẫn được sử dụng theo thời gian để tuân thủ thói quen ObjC.
Maxthon Chan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.