8

MonoDevelop suggests turning this:

if (someBoolVar)
    anotherBoolVar = true;

into this:

anotherBoolVar |= someBoolVar;

It also does it when I set anotherBoolVar to false instead:

if (someBoolVar)
    anotherBoolVar = false;

becomes:

anotherBoolVar &= !someBoolVar;

Can someone explain how these statements are equal?

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
foxneSs
  • 2,259
  • 3
  • 18
  • 21
  • 1
    I'm pretty sure the last one should be `&= ~someBoolVar;` you're missing the `~` – Lasse V. Karlsen Mar 26 '15 at 13:36
  • 15
    Your IDE wants you to write less readable code. Ignore its advice. – Frédéric Hamidi Mar 26 '15 at 13:37
  • 1
    `|` (or), `&` (and) and `~` (not) and are bitwise operators, in this case it can be shortened to these one-line assignments. Don't let the IDE "correct" your code if it confuses you :) – Binkan Salaryman Mar 26 '15 at 13:37
  • 1
    @foxneSs it's trying to suggest a (doubtly) micro-optimization (logical operations are faster than condition+set/jump) but any decent compiler will do better and it'll make your code _huh? why?_ so...disable that (if possible) and ignore such suggestions – Adriano Repetti Mar 26 '15 at 13:38
  • @LasseV.Karlsen yeah I was missing negation but it was "!" – foxneSs Mar 26 '15 at 13:38
  • @BinkanSalaryman `|` and `&` are ONLY bitwise operators for integral types - they are _boolean_ operators for `bool`. – D Stanley Mar 26 '15 at 13:39
  • Yes, of course, `~` is for bitmasks. Corrected my answer as well. I've been writing enum code all day so I was hung up on `~` :) – Lasse V. Karlsen Mar 26 '15 at 13:41
  • @D Stanley I consider `boolean` to be an integer with 1 bit (though managed in a byte) - you might call it *boolean* operator if you prefer that^^ – Binkan Salaryman Mar 26 '15 at 13:41
  • As @FrédéricHamidi said: If you have to ask what that is, probably your co-workers will have to ask it too, or at least look at the docs. I say, ignore it :) – Balázs Édes Mar 26 '15 at 13:45

6 Answers6

12

Well, functionally they're equivalent.

In the first case you want to set anotherBoolVar to true if someBoolVar is true, regardless of what value anotherBoolVar currently has, the replacement expression does that.

It is short for this:

anotherBoolVar = anotherBoolVar | someBoolVar;

The second replacement also does the same as the code it replaces, and is short for this:

anotherBoolVar = anotherBoolVar & (!someBoolVar);

The solution is hidden in the "bitwise" nature of boolean variables in this case. To and with an inverted value (~ inverts the someBoolVar) will effectively say "keep all bits that are set in !someBoolVar and clear out the rest", meaning that if someBoolVar is true, it will be inverted to false, and you'll effectively clear out anotherBoolVar.


Now, should you do this ?

In my opinion, no. The code is more readable as-is. Keep, and possibly even look for a way to ask MonoDevelop to not suggest these things in the future.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 1
    Emphasis on SHOULD you do this. No. Just like other robots, the IDE has no emotional connection to the code and this IDE apparently cares not about its human users. :) – kingdango Mar 26 '15 at 13:44
  • 2
    It seems MonoDevelop is working on the assumption of a _dumb_ compiler. Any _decent_ compiler will optimize (even at `-O0`) the `if` statement into an `OR` or `AND` instruction to avoid a "compare-and-jump". On the upside, [MonoDevelop is open source](https://github.com/mono/monodevelop), so if you have a GitHub account, you can file a bug (issue) request (@LasseV.Karlsen you should mention that in your last paragraph) – Cole Tobin Mar 26 '15 at 14:45
3

IDE code recommendations are often a double edge sword. Yes, the statements are terser BUT they are also potentially confusing (hence your pondering).

if (someBoolVar)
    anotherBoolVar = someBoolVar;

is the same as

anotherBoolVar |= someBoolVar;

because |= short-circuts if someBoolVar is false. If someBoolVar is true then it does not short-circut and therefore assigns the someBoolVar value (true) to anotherBoolVar.

Although the more terse statement may be slightly optimized I recommend you stick with your if statement because it is more expressive and readible.

For these type of if statements I try to keep them on one line:

if (someBoolVar) anotherBoolVar = someBoolVar;

kingdango
  • 3,979
  • 2
  • 26
  • 43
  • 1
    +1 But you should add a boolean logic table similar to what [Ross Pressner did](http://stackoverflow.com/a/29279979/1350209). It'll help explain WHY they are equivalent. Just saying they are equivalent isn't much help to someone (now or in the future) who doesn't understand boolean logic (similar to a math class that just says `sin(x)^2 + cos(x)^2 = 1` without explaining _why_. – Cole Tobin Mar 26 '15 at 14:54
2

For e.g.

if (someBoolVar)
    anotherBoolVar = true;

So when someBoolVar is true it comes down to

anotherBoolVar = (whatever boolean value of anotherBoolVar) | true;

which will always evaluate to true.

Nikhil Agrawal
  • 47,018
  • 22
  • 121
  • 208
2

For the first change, the if construction means:

someBoolVar is true ==> anotherBoolVar becomes true
someBoolVar is false ==> anotherBoolVar is unchanged

The |= construction means:

someBoolVar is true ==> anotherBoolVar becomes (true | initial value) which is always true
someBoolVar is false ==> anotherBoolVar becomes (false | initial value) which is always equal to the initial value

Similar remarks apply to the second change, although as @Lasse V. Karlsen mentioned, the &= construction seems to be missing a tilde.

Ross Presser
  • 6,027
  • 1
  • 34
  • 66
  • _"...seems to be missing a tilde"_ FYI, it's called (at least by me) a "_bitwise NOT_ operator". Others may call it something slightly different. – Cole Tobin Mar 26 '15 at 15:02
  • Oh come on. There is a difference between a tilde and a bitwise NOT operator, just as there is a difference between a summation symbol and the Greek letter sigma. You can't call me wrong for using the typographical name instead of the semantic name. – Ross Presser Mar 26 '15 at 15:55
  • If you reread my comment, I _didn't_ call you wrong. I was just saying "bitwise NOT operator" is language agnostic, but "tilde" is specific to C based languages. – Cole Tobin Mar 26 '15 at 17:56
  • Sorry for flying off the handle. – Ross Presser Mar 26 '15 at 19:25
1

Maybe the Wikipedia article on Boolean algebra helps.

The suggestion is a micro-optimization that can turn macro in a hurry. The just-in-time compiler generates a conditional branch for the if() statement, at least the ones created by Microsoft are not smart enough to optimize the code themselves. You'd have to have a look-see if the Mono jitter can do a better job by looking at the generated machine code. Typical code generation looks like:

            if (someBoolVar) anotherBoolVar = true;
00007FFD989F3BB1  movzx       eax,cl  
00007FFD989F3BB4  test        eax,eax  
00007FFD989F3BB6  je          00007FFD989F3BBA    // <=== here
00007FFD989F3BB8  mov         dl,1  
00007FFD989F3BBA  etc...

Conditional branches like the JE instruction in the above machine code are troublesome to a modern processor, it relies heavily on the pipeline to make code execute fast. Instruction decoding and micro-ops generation is done ahead of time. Also a really big deal to the prefetcher, it tries to guess what memory locations need to be available in the caches so the execution engine doesn't stall when the memory content is needed. Memory is very, very slow compared to the raw execution speed of the processor's execution engine.

A processor has a branch predictor, it keeps track of whether the branch was taken when the code previously executed. And assumes that the branch will behave the same way again. If it guesses wrong then the pipeline needs to be flushed. A lot of work is thrown away and the processor will stall while it fills back up. Additional long stalls can occur if the prefetcher guessed wrong, high odds that it did. There is a good SO post that explains the consequences of a wrong prediction.

Using the boolean algebra avoids the branch, it will generate an OR or AND instruction, they take a single cycle and can never flush the pipeline. Certainly a micro-optimization, it only turns macro when this code is located inside the ~10% of your code that determines the speed of your program. The IDE won't be smart enough to tell you whether that's the case, only a profiler can show you that.

Fwiw, there are more micro-optimizations like that, programmers tend to use the && and || operators inappropriately. The short-circuiting behavior of those operators always requires a branch in the machine code. That behavior is not always needed, usually it isn't and the & and | operator can generate far faster code. If the left side operand is poorly predicted then it can make code 500% slower.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0
if (someBoolVar)
    anotherBoolVar = true;

is equivalent to

if (someBoolVar)
    anotherBoolVar = true;
else
    anotherBoolVar = anotherBoolVar //Nop

which is equivalent to

if (someBoolVar)
    anotherBoolVar = anotherBoolVar | someBoolVar;
else
    anotherBoolVar = anotherBoolVar | someBoolVar //Nop

because x | true == true and x | false == x

and I assume you know that

anotherBoolVar |= someBoolVar

is equivalent to

anotherBoolVar = anotherBoolVar | someBoolVar
James
  • 9,774
  • 5
  • 34
  • 58