25

When I first leared how to write programs, I used C. (very basic command line applications) In both languages you use the ! - operator normally like this:

    if(!true){
       //false..
}

I wanted to do some bit-masking in c# and i wondered about the '~' operator.

Now i'm a bit confused because in my own understanding ! and ~ should do the same thing.

This works in c#:

int i = ~0xffffffff; // i = 0
bool j = !true; // j = false

This doesn't: (But it does work in c and does exactly what i expected)

int i = !0xffffffff; // i = 0

So what is the diffrence between ~ and ! and why did they separate them?

Marc Trittibach
  • 371
  • 3
  • 8
  • Assume `true == 0x00000001`, and the difference should be more clear when you run `~0x01` and `!0x01`. – ssube Jan 15 '14 at 16:21

3 Answers3

43

C# made the decision to completely separate integer operations from boolean operations. You cannot for example do if(x & 4) you must do if((x & 4) != 0) to explicitly move from integers to booleans.

This is in line with over 4 decades of experience with C and its predecessors in which people have often made mistakes with it such as &ing two values that had a truth-value of true and getting false because while they where both non-zero they had no non-zero bits in common.

C and C++ both introduced a bool type late in their history to add a more explicit difference between integers that represent either numbers or bit-patterns, and values where we only care about truth-values, but had to be compatible with older code. C# had the luxury of being even more explicit.

With that in mind, C#'s ! and ~ are exactly the same as in C, except that some things no longer make sense:

In C ! negates truth value. It turns 0 (false) into 1 (true) and everything non-zero (true) into 0 (false). In C# that only makes sense with bool, not with int.

In C ~ produces the one's-complement; It produces a value where every 1 bit is turned to 0 and every 0 bit is turned to 1 (e.g. 0xFFFFFFFF becomes 0, 0xF0F0F0F0 becomes 0x0F0F0F0F and so on). In C# that makes sense with int, but not with bool.

If you want to do the equivalent of !someInteger in C#, do someInteger == 0.

Edit:

It's worth noting that there is sometimes some confusion caused by the operators being split into "bitwise" ('&', '|' and '~') and "boolean" ('&&', '||' and '!'). This distinction is not quite correct.

Now, the last three do indeed only make sense in boolean contexts, and so with C# having a stricter separation between boolean and integer values, they are no longer applied to integers.

'~' does indeed not make sense in boolean contexts ('~x' where 'x' is true will produce an 'x' that is still true, 4294967294 times out of 4294967295), and so with C# it's no longer applied to bools.

'&' and '|' retain a boolean use though. In the case where 'A()' and 'B()' each return a bool, then while A() && B() will only call B() if A() is false (that is, it "short-circuits"), A() & B() will always call both methods, before doing the Boolean arithmetic. This tends to be rare because:

  1. Most of the time calling B() is just a waste of time, and short-circuiting can give us a performance boost ranging from massive (if B() is expensive) through to made-no-difference-but-we-didn't-lose-anything-anyway, so it's the habit to be in. (But consider that if B() is very cheap the cost of calling it anyway might be cheaper than the branch, especially if mispredicted, see comments below).

  2. Sometimes && is compulsory such as in x != null && x.Length != 0 where not short-circuiting would throw an exception on the second argument.

  3. If it's that important to make sure both methods were called, then it's better coding to do this in separate statements, to make that clear to other developers (or yourself when you come back later).

But if we're going to talk about the difference between operators with boolean and integer arguments, we should include the boolean use of | and &, because they do come up (sometimes through typos!), and they can cause confusion if people are falsely separating "bitwise operators" and "boolean operators" and forgetting that there are two symbols that are used as both.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • Thank you! The other answers were alright to, but not really what I wanted to hear. Because an invert of an "one-bit"(As far as I know is a bool technically saved like 0xFF or 0x00) type should be the same like an invert of an integral type. – Marc Trittibach Jan 15 '14 at 10:17
  • @MarcTrittibach `0xFF` is not "one bit", it's one byte (because it's the shortest adressable memory unit)! According to it's spec, the CLI treats everything as `true` where _one or more_ bits are set. So if you have `0x01` (which is true) and invert it it'll be `0xFE`. As there are still bits set, it's still true! Refer to [this answer](http://stackoverflow.com/a/3335872/1807643). – KeyNone Jan 15 '14 at 10:38
  • 1
    Actually, in C the normal value of (!0) is 0x01. Internally this is true in C# too (you can test this with unsafe operations copying from a `bool*` to a `int*`). For this reason using `~` for one's-complement (or `-` for two's-complement) on values used as bools but which aren't bools, can give confusing results, hence also [allowing ++ on bools, but not --](http://stackoverflow.com/questions/3450420/bool-operator-and/3450592#3450592). In VB6 and earlier, true bools were indeed 0xFFFF for reasons close to your thinking, but that could still cause confusion... – Jon Hanna Jan 15 '14 at 10:39
  • 1
    ... e.g. while this now allows for greater consistency between operations on bools and integers (NOT TRUE results in FALSE without any different means of operations, because ~0xFFFF is 0x0), there's still the issue that 4 is true and 2 is true but 4 AND 2 is false. Importantly, in C `!1`, `!0xFFFFFF` and `!42` all result in 0, not corresponding with `~`. – Jon Hanna Jan 15 '14 at 10:41
  • @BastiM oops I missinterpreted this text from www.dotnetpearls.com/bool. I thought I read if its true its 0xFF but it's just anyting but 0x00: "A CLI Boolean type occupies 1 byte in memory. A bit pattern of all zeros denotes a value of false. A bit pattern with any bit set (analogous to a non-zero integer) denotes a value of true. Miller & Ragsdale, p. 479" – Marc Trittibach Jan 15 '14 at 10:46
  • 1
    Yes, any non-zero is treated as `true`, but the result of operations that **produce** `true` (e.g. `!false`) is always 1. Indeed, I've found it's bad to force other non-zero into serving as `true`; this isn't actually possible in C# and most .NET languages, and some code out there assumes that the only possible values are `1` and `0`. I had an interesting bug last week in some CIL I was writing where I did a C-style any-non-zero-is-true approach and my NUnit test said "Expected: True But was: True". Strictly the bug was in `object.Equals`, but I changed it because other code could call that. – Jon Hanna Jan 15 '14 at 10:56
  • I wish there were either some operator which would be equivalent to `((x & y) != 0)`, or that `&` would return a compiler-internal type which would be implicitly converted to either `int` or `bool` as needed. When testing for bits in an enumeration, I don't think the required `!= 0` really improves legibility. I know MS added `enum.HasFlags`, but replacing a `AND` or `TEST` instruction with Reflection seems really icky. – supercat Jan 15 '14 at 16:13
  • Just a note about the short-circuit operations - there is another case you would want that behaviour - when the right hand side has side-effects. For example if you want to count true things, or if the right hand side opens a file whose existence is tested by the left hand side, etc. – Julian Jan 15 '14 at 18:13
  • 2
    Good answer Jon. I would add one other point. `A() & B()` can be *higher* performance than `A() && B()` in some rare cases. The `&&` is equivalent to an `if-then`, which not only makes the code larger, it introduces a conditional branch, which means that the processor has to do branch prediction, which means it can choose wrong, which means the processor can make other bad optimization decisions. If `B()` is extremely cheap then sometimes the cost of doing it unnecessarily is *smaller* than the cost of adding an extra conditional branch. – Eric Lippert Jan 15 '14 at 22:14
  • And of course that's just one aspect of how things can go wrong. Conditional branches in the IL also make more work for the jitter. Which means the jitter can take longer. Or the jitter might generate worse code. Or the jitter might decide to skip inlining a small method. And so on. – Eric Lippert Jan 15 '14 at 22:15
  • Good point @EricLippert, and I should have remembered my "we-didn't-lose-anything" above was wrong, though probably still the one to be in the habit of doing, with the other for hot-spot optimisation. A lot of the time we're picking a branch anyway rather than doing further boolean arithmetic, which I imagine (but don't know for sure) reduces the issue. I wonder (and ask out loud to anyone reading) if simple side-effect-free RHSs of `&&` would ever be performed for this reason, whether by the chip, the jitter or any compilers (I imagine its' against the C# compilation philosophy). – Jon Hanna Jan 15 '14 at 22:28
  • @supercat I don't think I find the `!= 0` ever increases readability, though that could say as much about my misspent childhood than the form itself. It does add explicitness, and I don't think it's the worse thing to pay for it. If nothing else, while I wouldn't often make logical errors with it, I do sometimes have finger-slips that leave out the second `=` in `==`, which are now invalid. – Jon Hanna Jan 15 '14 at 22:43
  • @supercat: Were I given the chance to do it all over again I'd draw a strong distiction between "disjoint" enums and "flags" enums. Flags enums are essentially named bitfields; they have practically nothing in common with "disjoint" enums but all the operations on them assume that both are the same as an int. It would be much nicer to treat flags enums as an array of bools that just happen to have a particular compact format. – Eric Lippert Jan 15 '14 at 22:45
16

! is a boolean invert, it inverts a value from true to false and vice versa. http://msdn.microsoft.com/en-us/library/f2kd6eb2.aspx

~ is a bitwise invert, it inverts every bit of an integral value, like int i. http://msdn.microsoft.com/en-us/library/d2bd4x66.aspx

see http://msdn.microsoft.com/en-us/library/6a71f45d.aspx for full guide on all operators.

The reason why ! and ~ are separate is because they do different things.

thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110
2

~The bitwise NOT operator is a unary operator, as it includes single operand. Unlike the other bitwise operators, the bitwise version does not use the same symbol as the similar Boolean operator. To achieve a one's complement, the tilde character (~) is positioned to the left of the value to modify.

byte valueToComplement = 187;                  // 10111011  
byte complement = (byte) ~valueToComplement;   // Result = 68

! - is a boolean invert which can be either true or false.

Bhupendra Shukla
  • 3,814
  • 6
  • 39
  • 62