为啥按位运算符不如逻辑“和\或”运算符聪明

Posted

技术标签:

【中文标题】为啥按位运算符不如逻辑“和\\或”运算符聪明【英文标题】:Why aren't bitwise operators as smart as logical "and\or" operators为什么按位运算符不如逻辑“和\或”运算符聪明 【发布时间】:2014-07-01 07:48:39 【问题描述】:

我刚刚注意到按位运算不像逻辑“和\或”运算那样“聪明”,我想知道为什么?

这是一个例子:

// For the record
private bool getTrue()return true;
private bool getFalse()return false;

// Since a is true it wont enter getFalse.
bool a = getTrue() || getFalse(); 

// Since a is false it wont enter getTrue.
bool b = getFalse() && getTrue(); 

// Since b is false it wont enter getTrue.
b = b && getTrue();

但是位运算符“|=”和“&=”没有那么聪明:

bool a = getTrue();
a |= getFalse(); // a has no chance to get false but it still enters the function.

a = getFalse();
a &= getTrue(); // a has no chance to get true but still performs this operation.

我想知道为什么它们不能以相同的逻辑方式工作。

【问题讨论】:

【参考方案1】:

一个澄清:

&=|= 运算符在 bools 上计算时不是按位运算符 - 它们是逻辑运算符,但它们等效于 x = x & yx = x | y,它们不会像 @ 那样短路987654327@ 和 || 做。

来自MSDN:

& 运算符对整数操作数执行按位逻辑与运算,对布尔操作数执行逻辑与运算。

设计者可以实现||=&&=,但由于它们适用于布尔类型,因此没有太大价值。

【讨论】:

在 C# 中像 2 & true 这样的操作会发生什么?那就是当您正在评估的 & 同时具有 int 和 bool 操作数时。 & 是为两个 int 操作数或两个 bool 操作数定义的,但不是每个操作数之一,因此需要将其中一个操作数转换为 bool 或 int 以便它们匹配,对吗?所以,我的问题是:2 & true 会在 C# 中变成 int & int aka 2 & 1 aka 0(如果转换为布尔则为 false)或 bool & bool aka true & true aka true 吗?跨度> @Apriori 2 & true 不会编译。在 C# 中,您不能将 bool 转换为 int,反之亦然。【参考方案2】:

D Stanley's answer 是正确的;您的错误在于将& 应用于bool 时将其视为“按位”。当应用于bool 时,最好将&&& 视为逻辑AND 的急切和惰性版本。

现在这是一个你没有问的问题,但实际上是更有趣的问题:

首先为什么bool 有一个非短路版本的AND 和OR?也就是说,为什么你会说exprX & exprY 而不是exprX && exprY

不好的原因是:表达式exprY 可能有一个副作用,无论exprX 的值如何,您都希望始终发生这种情况。这是一个不好的理由,因为将表达式用于其副作用和价值是一种有问题的做法。

很好的理由是:计算&&& 更快。

这怎么可能?当然,如果我们可以避免在某些时候计算右手边,那么我们可以总是平均节省时间,对吧?

错错错。 z = x && y 将生成为具有以下结构的代码:

if x goto CONSEQUENCE
z = false
goto DONE
CONSEQUENCE: z = y
DONE: 

与简单地计算 x & y 并将结果分配给 z 相比,这是很多指令,而且大代码需要更多时间从磁盘加载,更多时间进行 jit 编译,并占用更多处理器缓存空间.

此外,这些指令包含一个条件分支和一个非条件分支,大大增加了抖动必须处理的基本块的数量。 (“基本块”是一段具有明确开始和结束的代码,这样如果没有异常,基本块中的所有代码都会执行。)当基本块的数量增加时,抖动可能会选择避开某些优化必须分析变得太大。

最糟糕的是,任何条件分支都会让 CPU 有机会让它的分支预测器做出错误的选择,这在某些情况下可能会对性能产生严重影响。

现在,这并不是说您永远不应该将&& 用于布尔值;目前还没有任何程序在市场上取得巨大成功可归功于这种纳米优化的使用。我指出这一点仅仅是因为不完全清楚为什么在布尔值上应该有一个非短路逻辑运算符。

【讨论】:

非常有趣,这是我过去一直想知道的!虽然现在我很好奇为什么该功能会出现在“已实现”列表中,而不是在“很高兴拥有”下苦苦挣扎。 @BenAaronson:嗯,首先这是一个非常简单的功能;如果您已经有任何二元运算符,那么再添加一个并不难。其次,在一个任意类可以具有用户定义的 & 运算符但 bool 没有运算符的世界中,这会很奇怪,你不是说吗? @EricLippert 感谢您的肯定——现在,如果您能证实我的猜想,即实施&&=||= 的价值不值得付出代价,我可以让一个快乐的人退休。 :) @DStanley:奇怪的是,当我在愚人节写一篇关于疯狂复合运算符的帖子时,我什至没有想到包括&&=||=。 blogs.msdn.com/b/ericlippert/archive/2011/04/01/…这就是他们离我的想法有多远。 :-)【参考方案3】:

主要区别在于您使用的是|/& 还是||/&&。后者短路(你称之为“智能”),而前者没有(这是处理布尔类型时运算符之间的唯一区别)。由于没有 &&=||= 运算符,您只会注意到 &=|=,正如符号所示,它们不会短路。

我认为&=|= 可能存在,但&&=||= 不存在,因为前者用于数字类型,而短路没有意义。

【讨论】:

以上是关于为啥按位运算符不如逻辑“和\或”运算符聪明的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个逻辑/按位运算返回 1?

为啥 tslint 中不允许按位运算符?

&、|、^ 是按位运算符还是逻辑运算符?

c语言中按位逻辑运算符位移运算符

c语言中按位逻辑运算符位移运算符

Dan Bernstein 的 Djb2 哈希函数:当我们只能乘以 33 时,为啥还要使用按位运算符?