ตัวดำเนินการ XOR แบบโลจิคัลใน C ++?


292

มีอะไรแบบนี้เหรอ? มันเป็นครั้งแรกที่ผมพบความจำเป็นในการปฏิบัติสำหรับมัน แต่ฉันไม่เห็นหนึ่งที่ระบุไว้ใน Stroustrup ฉันตั้งใจจะเขียน:

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

แต่ไม่มี^^ผู้ประกอบการ ฉันสามารถใช้ bitwise ^ที่นี่และได้รับคำตอบที่ถูกต้อง (ไม่ว่าจะเป็นเครื่องจริงหรือเท็จ) ฉันไม่เคยผสม&และ&&หรือ|และ||ดังนั้นฉันลังเลที่จะทำอย่างนั้นด้วยและ^^^

ฉันจะสะดวกสบายมากขึ้นการเขียนของตัวเองbool XOR(bool,bool)ฟังก์ชั่นแทน


57
อันที่จริงจิมนั่นไม่ใช่ความแตกต่างเพียงอย่างเดียวระหว่าง & และ && เช่น ... 1 && 2 เป็นจริง แต่ 1 & 2 => 0 ด้วยเหตุนี้ฉันจึงคิดว่า "การลัดวงจร" เป็นเพียงคุณสมบัติที่พวกเขามี ตรรกะเป็นคุณลักษณะที่สำคัญอื่น ๆ ...
ไบรอัน Postow

6
ไม่พูดถึงว่า 2 && 3 == จริง แต่ 2 & 3 == 2
David Thornley

1
เดวิด Thomley: อืมใช่ แต่ 2 ==> จริงเพื่อให้ ok ของ ... จำไว้ว่ามีจริงๆไม่ booleans ใด ๆ ...
ไบรอัน Postow

13
@BrianPostow: จริง ๆ แล้วใน C ++ มี
Adrian Willenbücher

7
ดังที่โพสต์ด้านล่างนี่คือคำตอบของ Dennis Ritchie ว่าทำไมไม่มี: c-faq.com/misc/xor.dmr.html
Tobia

คำตอบ:


536

!=ผู้ประกอบการมีจุดมุ่งหมายนี้boolค่า


7
แต่ false! = false => false
Jonas

13
โปรดทราบว่านี่ใช้งานได้กับ booleans เท่านั้น และ ^ จะทำงานได้ดีอย่างสมบูรณ์ที่นั่น 2! = 1 => 1 ซึ่งไม่ใช่สิ่งที่คุณต้องการ! อย่างที่ LiraNuna พูดเอา! ด้านหน้าของทั้งสองฝ่ายแก้ปัญหาได้ แต่อีกครั้งแล้วคุณสามารถใช้บิต ^ ...
ไบรอัน Postow

8
ใช่ฉันระมัดระวังที่จะพูดถึง "สำหรับboolค่า" เพราะมันไม่จำเป็นต้องทำสิ่งที่คุณอาจต้องการสำหรับผู้ที่ไม่ใช่บูลีน และเนื่องจากนี่คือ C ++ จึงมีboolชนิดจริงอยู่แทนที่จะใช้intเพื่อจุดประสงค์นั้น
Greg Hewgill

29
หากคุณต้องการที่จะทำมันสำหรับประเภทaเพียงแค่เขียน!(a) != !(a)
คริสลัทซ์

6
@ChrisLutz: ใช่ แต่ระวังตัวดำเนินการมากเกินไป
rwong

246

สำหรับการดำเนินการ XOR แบบลอจิคัลที่เป็นจริงสิ่งนี้จะทำงาน:

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

หมายเหตุ!จะมีการแปลงค่าที่จะ booleans และปฏิเสธพวกเขาเพื่อให้จำนวนเต็มบวกที่ไม่เท่ากัน (แต่ละtrue) falseจะประเมิน


6
ฉันไม่เข้าใจว่าทำไม A และ B จึงถูกปฏิเสธ!
Martin Hansen

26
ส่วนใหญ่จะแปลงเป็นบูลีน !!จะทำงานได้ดีแค่ขอให้ แต่เนื่องจากพวกเขาจำเป็นต้องแตกต่างกัน
LiraNuna

4
คำถามคือคอมไพเลอร์สามารถเพิ่มประสิทธิภาพนี้อย่างถูกต้อง
einpoklum

6
การไม่ทราบความสำคัญของการทำให้ค่า bools เป็นมาตรฐานทำให้ฉันใช้เวลา 2 วัน
Makan Tayebi

9
@LiraNuna "ทำไม A และ B ถูกปฏิเสธด้วย!" / "ส่วนใหญ่จะแปลงเป็นบูลีน" - ฉันคิดว่านี่เป็นสิ่งที่ควรค่าแก่การพูดถึงในคำตอบ
cp.engr

42

การนำ XOR เชิงตรรกะที่เหมาะสมไปใช้ขึ้นอยู่กับว่าคุณต้องการเลียนแบบพฤติกรรมทั่วไปของตัวดำเนินการเชิงตรรกะอื่น ๆ ( ||และ&&) กับ XOR ของคุณอย่างไร มีสองสิ่งที่สำคัญเกี่ยวกับตัวดำเนินการเหล่านี้: 1) พวกเขารับประกันการประเมินผลการลัดวงจร 2) พวกเขาแนะนำจุดลำดับ, 3) พวกเขาประเมินตัวถูกดำเนินการเพียงครั้งเดียว

การประเมิน XOR อย่างที่คุณเข้าใจไม่สามารถทำให้เกิดการลัดวงจรเนื่องจากผลลัพธ์ขึ้นอยู่กับตัวถูกดำเนินการทั้งสอง ดังนั้น 1 หมดคำถาม แต่ประมาณ 2 ล่ะ หากคุณไม่สนใจเกี่ยวกับ 2 แล้วด้วยปกติ (เช่นboolผู้ประกอบการ) ค่า!=ไม่งานของแฮคเกอร์ในแง่ของผล และตัวถูกดำเนินการสามารถทำให้เป็นมาตรฐานได้อย่างง่ายดายด้วย unary !หากจำเป็น ดังนั้นจึง!A != !Bใช้ XOR ที่เหมาะสมในเรื่องนั้น

แต่ถ้าคุณสนใจเกี่ยวกับจุดลำดับพิเศษนั้นไม่ว่าจะไม่ใช่!=หรือ bitwise ^เป็นวิธีที่เหมาะสมในการนำ XOR มาใช้ วิธีหนึ่งที่เป็นไปได้ในการทำ XOR (a, b) อย่างถูกต้องอาจมีลักษณะดังนี้

a ? !b : b

นี้เป็นจริงใกล้ที่สุดเท่าที่คุณจะได้รับที่จะทำให้แฮคเกอร์โฮมเมด "ที่คล้ายกัน" ไปและ|| &&วิธีนี้ใช้ได้ผลแน่นอนถ้าคุณใช้ XOR เป็นแมโคร ฟังก์ชันจะไม่ทำเนื่องจากการเรียงลำดับจะไม่ใช้กับอาร์กิวเมนต์ของฟังก์ชัน

บางคนอาจพูดว่าเหตุผลเดียวที่มีจุดลำดับที่แต่ละจุด&&และ||เพื่อสนับสนุนการประเมินแบบลัดวงจรและทำให้ XOR ไม่ต้องการสิ่งใดสิ่งหนึ่ง มันสมเหตุสมผลแล้วจริงๆ ทว่ามันก็คุ้มค่าที่จะพิจารณาว่าจะมี XOR ด้วยจุดลำดับตรงกลาง ตัวอย่างเช่นการแสดงออกดังต่อไปนี้

++x > 1 && x < 5

ได้กำหนดพฤติกรรมและผลลัพธ์ที่ระบุใน C / C ++ (โดยคำนึงถึงการจัดลำดับอย่างน้อย) ดังนั้นหนึ่งอาจคาดหวังเหตุผลเดียวกันจากXOR ตรรกะที่ผู้ใช้กำหนดเช่นใน

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

ในขณะที่!=- ตาม XOR ไม่มีคุณสมบัตินี้


1
คุณพลาดสิ่งสำคัญอื่น ๆ เกี่ยวกับ||และ&&: C) พวกเขาประเมินตัวถูกดำเนินการในบริบทบูลีน นั่นคือ1 && 2เป็นจริง1 & 2ซึ่งแตกต่างจากที่เป็นศูนย์ ในทำนองเดียวกัน^^ผู้ประกอบการอาจมีประโยชน์สำหรับการให้บริการคุณสมบัติพิเศษนี้ในการประเมินตัวถูกดำเนินการในบริบทบูลีน Eg 1 ^^ 2เป็นเท็จ (ไม่เหมือน1 ^ 2)
Craig McQueen

2
@ Craig McQueen: ฉันจะไม่คิดถึงมัน ย่อหน้าที่สองของโพสต์ของฉันกล่าวถึง ในความคิดของฉันการรักษาตัวถูกดำเนินการเป็นค่าบูลีนไม่ได้เป็นคุณสมบัติที่สำคัญของผู้ประกอบการตรรกะในแง่ที่ว่าพวกเขาจะไม่ได้รับการแนะนำด้วยเหตุผลเพียงอย่างเดียว เหตุผลหลักที่แนะนำคือการประเมินแบบลัดวงจรและจุดลำดับที่จำเป็นสำหรับสิ่งนั้น
AnT

ทุกวันนี้ข้อเสนอแนะของคุณจะยังทำงานกับแมโครได้หรือไม่ แม้ว่าจะเป็นความจริงที่ลำดับของพารามิเตอร์ที่จะประเมินในฟังก์ชั่นนั้นขึ้นอยู่กับคอมไพเลอร์ แต่ปัจจุบันนี้หายากหรือไม่ที่จะแตกต่างจากซ้ายไปขวา นอกจากนี้ยังอาจมีการบันทึกที่นี่ในความคิดเห็นว่าหากการดำเนินการดูเหมือนว่า#define XOR(ll,rr) { ll ? !rr : rr }แล้วโทรเช่นint x = 2; XOR(++x > 1, x < 5);จะให้ผลที่ไม่ถูกต้อง การโทรจะต้องมีวงเล็บพิเศษอย่างint x = 2; XOR( (++x > 1), (x < 5) );ในเพื่อที่จะให้ผลลัพธ์ที่ถูกต้อง
RAs

1
เนื่องจาก XOR ไม่สามารถลัดวงจรจึงไม่จำเป็นต้องมีจุดเชื่อมต่อ แฮคเกอร์มีความหมายมากกว่า + ในเรื่องนั้น หากคุณไม่ต้องการโต้แย้งสำหรับ (++ x) + x เท่ากับ 2x + 1 จุดลำดับไม่เหมาะสม
hkBst

1
@hkBst: ฉันคิดว่านี่เป็นคำตอบที่ครอบคลุมทั้งหมดในส่วนที่สอง
AnT

25

มีอีกวิธีในการทำ XOR:

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

ซึ่งเห็นได้ชัดว่าสามารถแสดงให้ทำงานผ่าน:

#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
วิธีนี้สามารถสร้างรหัสที่ค่อนข้างช้า - 3-5x ช้ากว่า (! a)! = (! b) ทั้ง clang และ gcc โดยใช้ libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon

18

ตัวดำเนินการ XOR ไม่สามารถลัดวงจรได้ เช่นคุณไม่สามารถทำนายผลลัพธ์ของนิพจน์ XOR ได้เพียงแค่ประเมินตัวถูกดำเนินการทางซ้าย ดังนั้นจึงไม่มีเหตุผลที่จะให้^^รุ่น


25
-1 เนื่องจากความแตกต่างหลักระหว่าง && และ & ไม่ใช่เพียงการลัดวงจร 1 && 2 เป็นจริง แต่ 1 & 2 เป็นเท็จ การลัดวงจรเป็นเพียงผลข้างเคียงที่มีประโยชน์
Brian Postow

6
คำตอบไม่ได้พูดถึง&&และ&ที่ทั้งหมด ^^จุดของมันคือว่ามีเหตุผลที่จะแนะนำไม่มี สถานที่ให้บริการที่^^จะพิจารณาค่าที่1ไม่เป็นโมฆะใด ๆ ที่ไม่เป็นประโยชน์จริงๆฉันสงสัย หรืออย่างน้อยฉันก็ไม่เห็นประโยชน์ใด ๆ
Johannes Schaub - litb

6
นอกจากนี้ C ++! = ภาษาอื่น ๆ ใน C และ C ++ ดังที่แสดงไว้ข้างต้นการลัดวงจรไม่ได้เป็นเพียงสิ่งที่ดีที่จะมี แต่มันเป็นสิ่งสำคัญพื้นฐาน :)
Johannes Schaub - litb

13
จากนั้นคุณควรอ่านคำตอบของ Dennis Ritchie ถึงสาเหตุที่ไม่มี: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb

5
นี่คือลิงก์ที่ใช้งานได้สำหรับคำตอบของเดนนิสริชชี่ว่าทำไมมันถึงไม่มีอยู่: c-faq.com/misc/xor.dmr.html
Tobia

13

มีการโพสต์โค้ดที่ดีซึ่งแก้ไขปัญหาได้ดีกว่า! a! =! b

โปรดทราบว่าฉันต้องเพิ่ม BOOL_DETAIL_OPEN / CLOSE ดังนั้นจึงสามารถใช้งานได้ใน 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


5

นี่คือวิธีที่ฉันคิดว่าคุณเขียนการเปรียบเทียบ XOR ใน 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
}

พิสูจน์

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

หลักฐานคือการศึกษาอย่างละเอียดของอินพุตและเอาต์พุตแสดงให้เห็นว่าในตารางสองตารางสำหรับทุกชุดอินพุตผลลัพธ์จะเหมือนกันเสมอในตารางสองตาราง

ดังนั้นคำถามเดิมเป็นวิธีการเขียน:

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

คำตอบก็คือ

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

หรือถ้าคุณต้องการเขียน

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

! a! =! b ดูเหมือนว่าจะดีกว่าเพราะจะแปลงอาร์กิวเมนต์ของคุณเป็นบูลสำหรับคุณ
xaxxon

3

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

ส่วนแรกคือ A หรือ B ซึ่งเป็นแบบรวมหรือ ส่วนที่สองคือไม่ใช่ A และ B คุณจะได้ A หรือ B ร่วมกัน แต่ไม่ใช่ทั้ง A และ B

นี่จะให้ XOR ที่พิสูจน์แล้วในตารางความจริงด้านล่าง

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

ฉันไม่ได้ขุดประสิทธิภาพในวิธีนี้: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon

ไม่จำเป็นต้องมีการป้องกันเกี่ยวกับเรื่องนี้
xaxxon

1
@xaxxon: ไม่เห็นจุดในเกณฑ์มาตรฐานนี้! ใน StringCreation คุณใช้ในการสร้างสตริง แต่ในเกณฑ์มาตรฐานที่สองคุณไม่ได้ หากคุณวางรหัสที่เหมือนกันในการวัดประสิทธิภาพทั้งสองและเรียก XOR ที่แตกต่างกันสำหรับการวัดประสิทธิภาพแต่ละครั้ง (XOR และ XOR2) คุณจะได้รับผลลัพธ์การวัดประสิทธิภาพเดียวกัน ดังนั้นคุณพยายามพูดอะไร
SoLaR

1

ผมใช้ "แฮคเกอร์" (ดูเหมือนว่ามันเป็นคำหลักนั้นในรหัส :: บล็อกอย่างน้อยจะได้รับตัวหนา) เช่นเดียวกับที่คุณสามารถใช้ "และ" แทน&&และ "หรือ" ||แทน

if (first xor second)...

ใช่มันเป็นบิต ขอโทษ


2
ฉันเดาว่าสิ่งเหล่านั้นถูกซ่อนไว้ #defines จากที่อื่น ฉันค่อนข้างแน่ใจว่า "และ" และ "xor" ไม่ใช่คำหลักใน ansi C ... (อย่างน้อยไม่ใช่ C79)
Brian Postow

1
@Brian Postow: ฉันไม่รู้ว่า C79 คืออะไร แต่ใน C ++ 98 andและxorเป็นมาโครไลบรารีมาตรฐาน ไม่ได้มาจาก "ที่ไหน" พวกเขามาจาก <iso646.h> มาโครเหล่านี้ยังอยู่ใน C99 (ไม่แน่ใจเกี่ยวกับ C89 / 90)
AnT

5
@ Brian Postow: ... xorยืนสำหรับบิต xor แม้ว่าในขณะที่andเป็นตรรกะและ
AnT

1
ฉันพิมพ์ C89 ผิดในขณะที่ C79 ... และและ xor ฯลฯ ไม่ได้อยู่ในสำเนา K&R ของฉัน ฉันไม่คิดว่าฉันเคยใช้ iso686.h อย่างน้อยก็ไม่รู้ด้วยซ้ำ ... และใช่พวกเขา #defines จากที่ไหนสักแห่งคุณเพิ่งรู้ว่าที่ไหนสักแห่ง B-)
Brian Postow

2
พวกเขาอยู่ใน C99 อย่างแน่นอนโดยใช้ส่วนหัวนั้น ใน C ++ พวกมันถูกรวมเข้ากับภาษาเป็น "โทเค็นทางเลือก" และคุณสามารถทำได้struct A { compl A() { } };เพื่อกำหนด destructor ตัวอย่างเช่น
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);
}

มันทำงานได้ตามที่กำหนดไว้ เงื่อนไขคือการตรวจสอบว่าคุณกำลังใช้Objective-Cซึ่งกำลังขอ BOOL แทนที่จะเป็นบูลหรือไม่ (ความยาวแตกต่างกัน!)


3
สิ่งนี้ละเมิดกฎขีดเส้นใต้คู่
Tamás Szelei

@ TamásSzeleiไม่จำเป็นต้องเป็นคอมไพเลอร์ไม่เห็นว่ามันจะถูกประมวลผลล่วงหน้าและในโลกของ Objective-C ขีดเส้นใต้คู่นั้นค่อนข้างธรรมดา
Maxthon Chan

จุดที่ดีเกี่ยวกับตัวประมวลผลก่อนถึงแม้ว่าฉันจะยังคงมีกลิ่นรหัสด้วยวิธีนี้ (ทำไมใช้แมโครแทนตัวพิมพ์ดีดล่ะ?) นอกจากนี้คำถามไม่ได้เกี่ยวกับวัตถุประสงค์ -C
Tamás Szelei

@ TamásSzeleiฉันเคยมีนิสัยที่จะแบ่งปันไฟล์ส่วนหัวในหลาย ๆ ภาษาและส่วนหัวทั้งหมดมาจาก Objective-C รหัสใหม่ของฉันไม่ได้กลิ่นมากเกินไปในขณะนี้ แต่ขีดเส้นใต้คู่ยังคงถูกใช้เป็นครั้งคราวเพื่อให้สอดคล้องกับพฤติกรรม ObjC
Maxthon Chan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.