The c# bitwise operators I shall be covering here and are of particular interest to me in my followup post are :
- binary OR(|) operator
- binary AND(&) operator
- XOR (^) operator
- Not (~) operator
Binary OR(|) operator
Binary | operators are predefined for the integral types and bool. For integral types, | computes the bitwise OR of its operands. For bool operands, | computes the logical OR of its operands; that is, the result is false if and only if both its operands are false.Lets focus on the integral types(types that can be represented as numbers) and how the computation takes place.
protected void Page_Load(object sender, EventArgs e) { byte a = 7; byte b = 9; int orComputed = a | b; Response.Write(string.Format( "<br />{0} | {1} OR computed :{2}", a, b, orComputed)); }
Output :
7 | 9 Result :15
To understand what just happened we need to look at bits and how each bit(which contains an integral type) is understood by the computer. The computer is going to understand this as binary data in the form of 0's and 1's eg. -->> 00101010(which represents the number 42), this is actaully much easier for a computer to handle since each bit(or integral type as in number), can be interpreted as either on or off signal.
0 = off
1 = on
To understand the bitwise operator with clarity and how it computes the result, lets first construct a base 2 table from right to left incrementing the exponent by 1 for each power. So, since we have 8 bits in a byte, we make a table that lists 8 elements 128, 64, 32, 16, 8, 4, 2, 1.
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Result |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 2 |
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 4 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 8 |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 16 |
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 32 |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 64 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 128 |
If you will look at the above pattern more closely, we notice how the 1 keeps moving 1 digit to the left for every new value in binary. This is also known as a "bit shift".
Ok, now that we have our binary 8bit table with digits to the power of 2^ up to 128, so for a simple 7 | 9, which yielded the result 15, lets look at how it was computed by first creating the binary representation using our base 2 table and then computing the binary | operation, so :
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Calculation | Result |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | (4+2+1) | 7 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | (8+1) | 9 |
0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | (8+4+2+1) | 15 |
As you can see from the above, binary (|) is being performed on each pair of corresponding bits. If a 1 is present in the either bit or in both bits, otherwise it's 0. Not clear ? Lets try that again. How did we get the result 1111 ? It's a simple comparision. If either bit stacked ontop of one another are 1 or both are 1, then the result is 1, otherwise the result is 0. So 1 on 1 = 1, 1 on 0 = 1 but 0 on 0 = 0.
Binary AND(&) operator
Quoting MSDN Docs :Binary & operators are predefined for the integral types and bool. For integral types, & computes the logical bitwise AND of its operands. For bool operands, & computes the logical AND of its operands; that is, the result is true if and only if both its operands are true.So, lets quickly test this :
protected void Page_Load(object sender, EventArgs e) { byte a = 7; byte b = 9; int orComputed = a & b; Response.Write(string.Format( "<br />{0} & {1} Result :{2}", a, b, orComputed)); }
Output is :
7 & 9 Result :1
Lets convert to binary using our base 2 table, and dig deeper :
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Calculation | Result |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | (4+2+1) | 7 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | (8+1) | 9 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | (1) | 1 |
so, how did we get the binary result 0001(1) ? binary (&) is being performed on each pair of corresponding bits. The comparision is made based on both bits stacked ontop of one another. if both are one's, then we have a 1, anything else is a 0. so 1 on 1 = 1, 1 on 0 = 0, 0 on 0 = 0.
Binary Xor (^) operator
Quoting MSDN Docs:
Binary ^ operators are predefined for the integral types and bool. For integral types, ^ computes the bitwise exclusive-OR of its operands. For bool operands, ^ computes the logical exclusive-or of its operands; that is, the result is true if and only if exactly one of its operands is true.A simple c# example :
protected void Page_Load(object sender, EventArgs e) { sbyte a = 7; sbyte b = 9; int orComputed = a^b;//on a negative Response.Write(string.Format( "<br />{0} ^ {1} Result :{2}", a,b, orComputed.ToString())); }
7 ^ 9 Result :14
Now, how did binary (^) Xor'ing 7 ^ 9 produce 14 ? This is because binary (^) is being performed on each pair of corresponding bits. If we have two matching zero's or one's, then the result is 0, otherwise 1. SO, 1 on 1 = 0, 0 on 0 = 0, 1 on 0 = 1, 0 on 1 = 1. Lets look at an example in our base 2 table of 7 and 9 in binary format :
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Calculation | Result |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | (4+2+1) | 7 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | (8+1) | 9 |
0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | (8+4+2) | 14 |
Not (~) operator
Quoting MSDN Docs:The ~ operator performs a bitwise complement operation on its operand, which has the effect of reversing each bit. Bitwise complement operators are predefined for int, uint, long, and ulong.This is also a unary operator so we don't need to pass a value. Unary operators perform an operation on a single operand eg. ~n and not x~y.
Binary (~) operator is the most confusing one. It performs reversing of each bit, so reversing a positive can produce a negative value. Negative Values Use Two's Complement format and is quite tricky.A simple example :
protected void Page_Load(object sender, EventArgs e) { sbyte a = 7; int orComputed1 = ~-a;//on a negative int orComputed2 = ~a;// on a positive Response.Write(string.Format( "<br />~-{0} Result :{1}", a, orComputed1)); Response.Write(string.Format( "<br />~+{0} Result :{1}", a, orComputed2)); }
~-7 Result :6
~+7 Result :-8
As you can see, when the NOT(~) operator is applied to a positive number, the resulting value is a negative. This is because the value is reversed. Since this reversing yields a negative or positive depending on what value your reversing, use a signed type (a type that can store a negative value).
In the base table below, our binary number gets inverted. all 1's become 0 and 0 becomes 1.
A signed binary uses the left most bit to keep track of the sign(negative or positive).
In the c# compiler, negative values are represented internally in two's complement format. Two's complement can be obtained by negating each bit of the value, then adding 1. Performing two's complement twice generates the original value.
In the following base 2 table, one typical gotcha when inverting is to remember to start the inversion from the left of the first 1 value in our binary. So, say had we a binary representation of the digit 7(00000111), then we start inverting at the 2nd digit starting at the right, skipping the first(since it's 1) and get 11111001. A last thing to also note is how in our calculation we added a negative -1 to our result instead of a positive 1 -->> (-128+64+32+16+8+1)-1 ; While the two's complement format states that we need add 1, we are actually deducting 1 versus adding 1. This is because we have to consider 0 in the equation.
An easy method to get the two's complement of +7 :(left most is used to track sign, so add -1 to the result) :
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Calculation | Result |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | (4+2+1) | 7 |
1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | (-128+64+32+16+8+1)-1 | -8 |
Note the left most significant pair of corresponding bits in red. A value of 0 means positive and a value of 1 means negative and is used for tracking the sign in a signed binary.
One question still remains, why is the two's complement always a positive less(6) and a negative more(-8) ?
Why aren't we getting -7 for a 7 and vice versa. I guess the answer to this is simply "logic", we have to consider the 0.
-8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Its excellent. Great job.
ReplyDeleteI think that it is very good article, too! In the good old days the bitwise operators were part of our everyday life. Now they are used only from the good devs which still think for performance.
ReplyDeleteP.S. Why not adding some words about << and >> just to cover the whole topic?
Thanks again!
it is not helpful as I am finding the operator for XNOR operation.
ReplyDeleteI encrypt the password using XOR operator, now i want to decrypt it. What is the process to achieve this?
gd stuff
ReplyDeleteBrad, if you XOR something, just XOR it again to get back your original value.
ReplyDeletei want the logic as how to write bitwise xor like
ReplyDeletefor 101 we get 0 by doing bitwise xor
for 100 we get 1 by doing bitwise xor
A GOOD STUFF INDEED.I want to add: If one XORs anything twice he will get the actual number again.That is how one can use it to encrypt and decrypt.
ReplyDeleteIf we want to get 0 for 101 and 1 for 100,it can be assumed that only the LSB is used for XOR operation,now if we XOR the number with 1,i.e.001 then we get the desired output.
ReplyDeletethis article really helps
ReplyDeleteMy Thanks to the author. This article is short but very, very clear:-)
ReplyDeletegreet job:)
ReplyDeletebut these are built in functions
i don't understand this article.
ReplyDeletefor example:
var a = 1;
var b = 2;
if (a == b ) {
//do sothing.
}
if i use bitwise operators, i will write:
if (a & b == a) {
//do sothing
}
is it true ? how about other operators ?
Please help me.
Cool use of the ~ operator is the binary search method in the List class. It returns a negative integer when it can't find the specified item. This negative integer becomes the correct insertion point to keep the list sorted, when you apply the ~ operator.
ReplyDeletePls help me..
ReplyDeleteI have to compute for binary Not AND(NAND), NOR and XNOR(Exclusive NOR)in C#. operator ~ and ! are not support for NAND, NOR and XNOR. so pls tell me how to ..
NAND (Not AND)
0 NAN 0 = 1
0 NAN 1 = 1
1 NAN 0 = 1
1 NAN 1 = 0
NOR (Not OR)
0 NOR 0 = 1
0 NOR 1 = 0
1 NOR 0 = 0
1 NOR 1 = 0
XNOR (Exclusive NOR)
0 XOR 0 = 1
0 XOR 1 = 0
1 XOR 0 = 0
1 XOR 1 = 1
Thanks, very well written and easy to understand post.
ReplyDeleteWaw gr8 examples,
ReplyDeleteIt's really helpfull for me.
Thankx
Exactly what I was looking for. Thanks!
ReplyDeletethanks..........
ReplyDeleteu hav done a great job....
i was trying to get it form months.........
thanks a lot>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Very impressive. Short, but to the point. I skipped class yesterday an apparently they spent the whole hour going over c# bitwise operators. Took ten min of reading this article to catch up lol.
ReplyDeleteThanks for writing such an understanding article on Bitwize
ReplyDeletereally helpful, thx. some tricks with ~, good explained. As asked: what's one's complement?
ReplyDeletegreat article & explanation (see what i did there)
ReplyDeleteBravo! I totally get it!!! I haven't worked with bit operations since college (many many years ago) and suddenly find myself on a project that requires it. You've put together an excellent explanation. Thank you!
ReplyDeleteThere are another two bitwise operators
ReplyDelete1. Right Shift: Right shift (>>) shifts all bits in a 32-bit number to
the right while preserving the sign (positive or negative); signed right shift is the exact opposite of left shift.
2, Left Shift: Left Shift shifts all bits in a number to the left by the number of positions given.
202 ^ 64541711
ReplyDeletehow does one XOR this?
I know it's a late comment, but i have to write it. In the last example, I don't understand why are you skipping first 1. The number -8 into binary is 11111000 not 11111001.
ReplyDeleteNice article. This is very useful. Thanks for sharing.
ReplyDeleteseo training in chennai