9

The NAND logic gate for two conditions (A and B) is true as long as either condition is true, or none of the conditions is true; and it is false if both conditions are true.

F NAND F = T
F NAND T = T
T NAND F = T
T NAND T = F

In C# I can write this in at least two ways:

!(A && B)

or

(!A || !B)

Which is more efficient?

It seems to me that if(A) then B is always tested and if(!A) then B is never tested.

But then the first condition must invert the result....

Matthew
  • 10,244
  • 5
  • 49
  • 104
  • 2
    inverting the result is the same as swapping the code in the `else` with the code in the `if`... no actual runtime instruction needed – Ben Voigt Aug 03 '13 at 21:37

3 Answers3

9

It is not quite as simple. The C# language doesn't model a NAND gate at all with the && operator. That operator has short-circuiting behavior, very convenient in a C# program. And common behavior for the curly-brace languages, an expression like this doesn't crash your code:

arr[] = somefunction();
int ix = foo;
if (ix < arr.Length && arr[ix] == 42) {
    EmailBookRecommendation();
}

But that's a very inefficient version of the expression, this one is way more performant:

if (ix < arr.Length & arr [ix] == 42) 

Which is a perfectly legal expression, the & operator works just fine with boolean operands. But unfortunately this one crashes your code. It evaluates the array indexing expression and that goes Kaboom! with IndexOutOfRangeException.

That's not ever a problem with a NAND gate, it doesn't crash when the first input is F :) There are many possible C# expressions where that's not a problem. You really should favor the & operator for those. It makes a huge difference. So always write something like this:

if (ix >= 1 & ix <= 42) 

Which of course can never fail. To understand why the && operator is so much more inefficient than the & operator, you have to understand branch prediction. That's covered very well in this answer.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • In my particular case, I am comparing integer values to literals. So it's something like `if(Foo.A==CONST1 NAND Foo.B==CONST2){Bar();}` So I could easily use the "&" rather than "&&" without procedural complication or slowdown. If I read you correctly it is more performant to use "&" and "predict" the resulting condition? I'm not quite sure I follow. – Matthew Aug 04 '13 at 03:44
  • 1
    Except that (1) the question didn't ask about `&`, and (2) `&&` is not "much more inefficient than the `&` operator", it depends completely on the expressions involved. – Ben Voigt Aug 04 '13 at 20:16
  • 1
    Can you elaborate on "huge difference"? – Piedone Jan 02 '17 at 12:41
  • I think the second answer in the question you linked to actually explains why && is good to use, or rather that sorting data intelligently pays dividends. But it depends on context. It certainly augments the first answer, and both (and the question) were informative. Why wouldn't you just write `(A !& B)` though? Assuming you're working with two boolean variables and not testing expressions that could have negative consequences. – Tim Jan 25 '21 at 18:41
4

Neither. They both have exactly the same short circuiting behavior, and the compiler will turn both into MSIL requesting a test of A, followed by a conditional branch. The branch where A was true will then have a test of B.

What you should be worrying about is:

!(A && B)

vs

!(B && A)

which are different in case either A or B causes side effects or complicated calculations.

The possible exception to both being the same is if you have a custom definition of operator!, in which case they aren't actually equivalent at all.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • assuming A and B are both `bool` then there is no functional difference? – Matthew Aug 03 '13 at 21:18
  • @Matthew: Assuming both `A` and `B` are local variables of type `bool`, there should not be any difference in the generated code. – Ben Voigt Aug 03 '13 at 21:19
  • I was thinking it would depend on the likelihood of A being `true` but you're telling me they compile to identical code? Interesting. – Matthew Aug 03 '13 at 21:21
  • @Matthew: The definition of `&&` and `||` both *require* the compiler to skip testing `B` if `A` is found to be false. – Ben Voigt Aug 03 '13 at 21:24
  • I don't think so... `A || B` will test B if A is `false` .. It will not test B is A is `true`. – Matthew Aug 03 '13 at 21:27
  • @Matthew: In your question I see `(!A) || (!B)`, not `A || B`. So it will test `B` if `(!A)` is false, which means only if `A` is true. – Ben Voigt Aug 03 '13 at 21:27
  • I have edited my question, trying to explain why your assertion that they compile to the same code is confusing. I accept that they have the same short-circuit... but then doesn't the first need an additional operation to invert the result? – Matthew Aug 03 '13 at 21:33
  • 1
    @Matthew: There are two blocks of code, a *then-part* and an *else-part*. For both, the compiler will jump straight to the *then-part* if `A` is true, otherwise test `B`, and jump to the *then-part* if `B` is true (if not, jump to *else-part*). There's no operation to invert an if-statement, it just switches the jump to either the *then-part* or the *else-part*. If you wrote `C = !(A || B);`, it might be different, but probably not. – Ben Voigt Aug 03 '13 at 21:35
  • @Matthew: If you do find any differences in the generated MSIL, please post them and we'll take a closer look. – Ben Voigt Aug 03 '13 at 21:41
  • 1
    This answer *completely* misses the point, the OP asked about *efficiency*. – Hans Passant Aug 04 '13 at 00:55
  • 1
    @Hans: If you can't understand that two pieces of source code which compile to the same thing therefore have the same efficiency... – Ben Voigt Aug 04 '13 at 20:14
-1

My short test of 10000 comparisons between the two says that the firsoneis faster.

it says the first needed totally of 1353 ticks and the second total of 1373 ticks.

EDIT: then again i tried this a few fore times and the results variate so they are both the same.

DaMachk
  • 643
  • 4
  • 10