C ++中的逻辑异或运算符?

Posted

技术标签:

【中文标题】C ++中的逻辑异或运算符?【英文标题】:Logical XOR operator in C++? 【发布时间】:2010-12-08 11:23:35 【问题描述】:

有这种事吗?这是我第一次遇到对它的实际需求,但我没有看到列出in Stroustrup。我打算写:

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

但是没有^^ 运算符。我可以在这里使用按位^ 并得到正确的答案(不管机器表示真假)吗?我从不将&&&||| 混合使用,所以我犹豫要不要使用^^^

我会更自在地编写自己的 bool XOR(bool,bool) 函数。

【问题讨论】:

实际上,Jim,这不是 & 和 && 之间的唯一区别,例如... 1 && 2 是 True。但是 1 & 2 => 0。因此,我认为“短路”只是他们碰巧拥有的属性。逻辑是更重要的特征... 不要说 2 && 3 == true,而是 2 & 3 == 2。 David Thomley:嗯,是的,但是 2 ==> 是的,所以没关系...记住,真的没有任何布尔值... @BrianPostow:实际上,在 C++ 中有。 如下所示,这是丹尼斯·里奇关于为什么它不存在的答案:c-faq.com/misc/xor.dmr.html 【参考方案1】:

!= 运算符为bool 值服务。

【讨论】:

但是假!=假=>假 请注意,这只适用于布尔值。 ^ 在那里工作得很好。 2 !=1 => 1 这不是你想要的!正如 LiraNuna 所说,放一个 !在双方面前解决了这个问题。但同样,你可以使用按位 ^... 对,我很小心地提到“对于bool 值”,因为它不一定能满足您对非布尔值的要求。由于这是 C++,因此存在一个真正的 bool 类型,而不必为此使用 int 如果你想输入a,只需写!(a) != !(a) @ChrisLutz:是的,但要小心重载运算符。【参考方案2】:

对于真正的逻辑异或运算,这将起作用:

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

注意! 用于将值转换为布尔值并将它们取反,以便两个不相等的正整数(每个true)的计算结果为false

【讨论】:

我不明白为什么A和B被否定了! 主要是将它们转换为布尔值。 !! 问得好就行,但由于它们需要不同,因此否定它们并没有害处。 问题是,编译器是否能够正确优化这一点。 不知道标准化布尔值的重要性让我花了 2 天时间。 @LiraNuna,“为什么 A 和 B 被否定了!” /“主要是将它们转换为布尔值。” - 我认为这值得在答案中提及。【参考方案3】:

正确的手动逻辑 XOR 实现取决于您希望用 XOR 模拟其他逻辑运算符(||&&)的一般行为的程度。这些操作符有两点很重要:1)它们保证短路计算,2)它们引入了一个序列点,3)它们只对它们的操作数进行一次计算。

正如您所理解的,XOR 评估不能短路,因为结果始终取决于两个操作数。所以1是没有问题的。但是2呢?如果您不关心 2,那么使用归一化(即bool)值运算符!= 在结果方面执行 XOR 的工作。如有必要,操作数可以很容易地用一元 ! 标准化。因此!A != !B 在这方面实现了正确的异或。

但如果您关心额外的序列点,!= 和按位^ 都不是实现 XOR 的正确方法。正确执行 XOR(a, b) 的一种可能方法如下所示

a ? !b : b

这实际上与制作与||&&“相似”的自制XOR 非常接近。当然,这只有在您将 XOR 实现为宏时才有效。函数不会这样做,因为排序不适用于函数的参数。

尽管有人可能会说,在每个&&|| 上都有一个序列点的唯一原因是为了支持短路评估,因此不需要异或。实际上,这是有道理的。然而,值得考虑在中间有一个序列点的 XOR。例如下面的表达式

++x > 1 && x < 5

在 C/C++ 中定义了行为和特定结果(至少在排序方面)。因此,人们可能会合理地期望用户定义的 logical XOR 相同,如

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

虽然基于 != 的 XOR 没有此属性。

【讨论】:

你错过了关于||&amp;&amp; 的另一个重要的事情:C)它们在布尔上下文中评估操作数。也就是说,1 &amp;&amp; 2 是真的,不像 1 &amp; 2 是零。同样,^^ 运算符可用于提供此额外功能,即在布尔上下文中评估操作数。例如。 1 ^^ 2 为假(与 1 ^ 2 不同)。 @Craig McQueen:我没有错过它。我帖子的第二段提到了它。在我看来,将操作数视为布尔值并不是逻辑运算符的一个关键特征,从某种意义上说,它们不会仅仅因为这个原因而被引入。引入它们的主要原因是短路评估和所需的序列点。 现在,您的建议是否仍然只适用于宏?尽管函数中要评估的参数顺序确实取决于编译器,但目前从左到右不同的情况不是很少吗?此外,在 cmets 中可能值得注意的是,如果实现看起来像 #define XOR(ll,rr) ll ? !rr : rr ,那么像 int x = 2; XOR(++x &gt; 1, x &lt; 5); 这样的调用将给出错误的结果。调用必须有额外的括号,例如 int x = 2; XOR( (++x &gt; 1), (x &lt; 5) );,才能给出正确的预期结果。 由于 XOR 不能短路,因此不需要序列点。在这方面,XOR 更像 +。除非你也想论证 (++x) + x 等于 2x+1,否则序列点是不合理的。 @hkBst:我认为这在我的回答的第二部分中已经完全涵盖了。【参考方案4】:

还有另一种异或的方法:

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;

【讨论】:

这种方法可以生成一些非常慢的代码 - 在使用 libstdc++ 的 clang 和 gcc 上比 (!a) != (!b) 慢 3-5 倍:quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q @xaxxon 您的基准测试不正确:基准测试的两个函数之一在其循环内创建了一个(不相关的)字符串。去掉这个,代码只慢了 30%。 quick-bench.com/q/umQRhhr0ZVS2o03fhCQAfN3HLak @gmargari 一定忘记删除它开始时自动生成的基准。对不起。【参考方案5】:

XOR 运算符不能短路;也就是说,您不能仅通过评估其左侧操作数来预测 XOR 表达式的结果。因此,没有理由提供^^ 版本。

【讨论】:

-1 因为 && 和 & 之间的主要区别不仅仅是短路。 1 && 2 为真,但 1 & 2 为假。短路只是一个方便的副作用。 答案根本不是&amp;&amp;&amp;。它的重点是没有理由介绍^^。我怀疑^^ 将任何非空值视为1 的属性并不是真正有用。或者至少我看不到任何用处。 还有 C++ != 其他语言。在 C 和 C++ 中,如上所示,短路不仅仅是一些不错的东西,而且它从根本上来说很重要。 :) 那么你应该阅读 Dennis Ritchie 关于它为什么不存在的答案:it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html 这是丹尼斯·里奇关于为什么它不存在的答案的工作链接:c-faq.com/misc/xor.dmr.html【参考方案6】:

发布了一些比 !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

【讨论】:

【参考方案7】:

使用简单的:

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

【讨论】:

【参考方案8】:

以下是我认为您在 C++ 中编写 XOR 比较的方式:

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 似乎更好,因为它会将您的参数转换为布尔值。【参考方案9】:

(A || B) &amp;&amp; !(A &amp;&amp; B)

第一部分是A OR B,即Inclusive OR;第二部分是,不是 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:在这个基准测试中看不到重点!?在 StringCreation 中,您用于创建字符串,但在第二个基准测试中您没有。如果您在两个基准测试中放置相同的代码,并为每个基准测试(XOR 和 XOR2)调用不同的 XOR,您将获得相同的基准测试结果。那你想说什么?【参考方案10】:

我使用“xor”(它似乎是一个关键字;在Code::Blocks 中至少它变得粗体)就像您可以使用“and”而不是&amp;&amp; 和“or”而不是||

if (first xor second)...

是的,它是按位的。对不起。

【讨论】:

我猜这些是从某个地方隐藏的#defines。我很确定“and”和“xor”不是ansi C中的关键字......(至少不是C79) @Brian Postow:我不知道 C79 是什么,但在 C++98 中 andxor 是标准库宏。他们不是来自“某处”,而是来自。这些宏也在 C99 中(不确定 C89/90)。 @Brian Postow: ... xor 代表 bitwise xor,而 andlogical and. 我将 C89 错误地输入为 C79... 并且 xor 等不在我的 K&R 副本中。我不认为我曾经使用过 iso686.h,至少在不知情的情况下......所以,是的,它们是来自某个地方的 #defines,你只是碰巧知道某个地方在哪里 B-) 他们肯定在 C99 中使用该标头。在 C++ 中,它们作为“替代标记”集成到语言中,例如,您可以使用struct A compl A() ; 来定义析构函数。【参考方案11】:
#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而不是bool(长度不同!)

【讨论】:

这违反了双下划线规则。 @TamásSzelei 不一定,因为编译器看不到这一点,因为它已被预处理掉,并且在 Objective-C 世界中双下划线相当普遍。 关于预处理器的好点,虽然对我来说它仍然是这样的代码气味(为什么要使用宏而不是 typedef?)。此外,问题与 Objective-C 无关。 @TamásSzelei 好吧,我以前有跨多种语言共享头文件的习惯,通常所有头文件都来自 Objective-C。我的新代码现在没有太多味道了,但是还是不时使用双下划线来遵守 ObjC 的习惯。

以上是关于C ++中的逻辑异或运算符?的主要内容,如果未能解决你的问题,请参考以下文章

c语言的按位运算符怎么操作!?

C语言中位移位运算符?

如何理解「异或(XOR)」运算在计算机科学中的重要性

如何理解「异或(XOR)」运算在计算机科学中的重要性

关于shell编程中逻辑运算异或的理解和实验

C语言位运算符:与或异或取反左移与右移详细介绍