MonoDevelop suggests converting if expressions to bitwise operations

MonoDevelop suggests including this:

if (someBoolVar) anotherBoolVar = true; 

in it:

 anotherBoolVar |= someBoolVar; 

It also does this when I set anotherBoolVar to false :

 if (someBoolVar) anotherBoolVar = false; 

becomes:

 anotherBoolVar &= !someBoolVar; 

Can someone explain how these statements are equal?

+8
c # bitwise-operators boolean-logic monodevelop
source share
6 answers

Well, functionally they are equivalent.

In the first case, you want to set anotherBoolVar to true , if someBoolVar is true , no matter what the value of anotherBoolVar , this is a replacement expression.

This is not true:

 anotherBoolVar = anotherBoolVar | someBoolVar; 

The second replacement also does the same as the code that it replaces, and is not suitable 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 someBoolVar ) will effectively say "save all the bits that are set to !someBoolVar and clear the rest", which means that if someBoolVar true, it will be inverted to false and you will clear anotherBoolVar .


Now if you do it?

In my opinion, no. The code is more readable as is. Hold on and maybe even look for a way to ask MonoDevelop not to offer these things in the future.

+12
source share

IDE code recommendations are often the double edge of a sword. Yes, statements are terms, but they are also potentially confusing (hence your thinking).

 if (someBoolVar) anotherBoolVar = someBoolVar; 

coincides with

 anotherBoolVar |= someBoolVar; 

because |= short-circuts if someBoolVar is false. If someBoolVar is true, then it does not close and therefore assigns the value of someBoolVar (true) to another BoolVar.

Although the shorter statement may be slightly optimized, I recommend that you stick with your if statement because it is more expressive and readable.

For these types of if statements, I try to keep them on the same line:

if (someBoolVar) anotherBoolVar = someBoolVar;

+3
source share

For example,

 if (someBoolVar) anotherBoolVar = true; 

So, when someBoolVar true , it comes down to

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

which will always be true.

+2
source share

For the first change, the if construct 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 noted, the &= construct does not have a tilde.

+2
source share

Perhaps a Wikipedia article on Boolean algebra helps.

A proposal is micro-optimization that can turn a macro into a rush. The just-in-time compiler generates a conditional branch for the if () operator, at least Microsoft-created ones are not smart enough to optimize the code itself. You will need to look - see if the Mono jitter can do a better job by looking at the generated machine code. Typical code generation is as follows:

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

Conditional branches, such as the JE command in the above machine code, are complex for a modern processor; it relies heavily on the pipeline to make the code run quickly. The generation of commands and the generation of micro-operations are performed in advance. It is also very important for the prefisher, he tries to guess which memory cells should be available in the caches, so the execution mechanism does not stop when the contents of the memory are required. The memory is very, very slow compared to the raw processor speed.

The processor has a branch predictor, it keeps track of whether a branch was taken when previously executed code. And assumes that the branch will behave the same again. If he suspects what is wrong, the pipeline needs to be cleaned. A lot of work is thrown away and the processor will stand while it is filling. Extra long stalls can occur if the picker guessed that it was. There is a good SO publication that explains the consequences of incorrect prediction.

Using Boolean algebra excludes a branch, it will generate an OR or AND instruction, they take one cycle and can never clear the pipeline. Of course, micro-optimization, it turns into a macro only when this code is inside ~ 10% of your code, which determines the speed of your program. The IDE will not be smart enough to tell you if it could be, only the profiler can show you this.

Fwiw, there are more such micro-optimizations, programmers tend to use && and || operators improperly. The short-circuited behavior of these operators always requires a machine code branch. This behavior is not always necessary, usually it is not, but the and and | the operator can generate much faster code. If the left operand is poorly predicted, it can make the code 500% slower.

+1
source share
 if (someBoolVar) anotherBoolVar = true; 

equivalently

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

which is equivalent

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

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

and I suppose you know that

 anotherBoolVar |= someBoolVar 

equivalently

 anotherBoolVar = anotherBoolVar | someBoolVar 
0
source share

All Articles