Page 1 of 2 Is C# a highlevel or a lowlevel language? One way of answering this contentious question is to simply find out how it deals with bits.
In a highlevel language there should be no need to provide lowlevel facilities.
Right?
Well not really.
There are all sorts of reasons why any language needs to get down to the level of the actual bits that make up any real program. Often it is forced on the programmer by the need to use legacy data or work with raw data generated by another program or hardware device.
Sooner or later you are going to have to solve a problem that comes down to working with the bit patterns that represent a variable’s value.
So how should a highlevel language handle such lowlevel work?
In the case of C# one important consideration is the need to stay in the realm of managed code. Of course it is possible to descend to almost any level if you are happy with “unsafe” and P/Invoke, but ideally C# should provide bit operations that are safe because they are part of managed code.
What is interesting about C# is that you can conclude that it isn’t very good at lowlevel bit manipulation until you discover the BitConverter class and a few other reasonably well hidden facilities.
Basic operations
The basic bit manipulation operations that are provided can be confusing because of the availability of similar operators specialised to Boolean data types. The bitwise operations are:
And

&

Or



Exclusive Or

^

Not

~

These all work on integer data types and return the corresponding bitwise operation.
For example:
int a = 0xF0, b = 0xFF; int c = ~a & b;
computes Not(a) and b.
It is also worth introducing the “x” format specifier as used in 0xF0 to enter a hexadecimal format number.
They don’t work on floating point types or custom types unless you go to the trouble of overloading the operators. They do also work on Booleans, however, and return just True or False according to the usual rules of the truth table. There are also three logical operators that only work with Boolean:
These only work with Boolean variables and return the same true or false results as the bitwise operators.
The And && and Or  operators are special because they are “lazy” evaluation operators. If you write:
bool x = false, y = true; bool w = x & y; bool v = x && y;
then w and v will have the same value. However, in the case of && the y variable will not be used at all because the fact that x is false means that the entire result has to be false no matter what values other variables have. In general: x&&y only evaluates y if x is true and xy only evaluates y if x is false.
This is in general not a question of efficiency but allows for the possibility that evaluating y when the condition was already determined by x would cause an error of some sort.
For example:
a = 0; if (a != 0 & (b / a) < .1) { };
throws an exception whereas:
a = 0; if (a != 0 && (b / a) < .1) { };
doesn’t.
In addition to the bitwise logical operators C# also has a left and right shift operator. For example:
a = a >> 8;
shifts the contents of a eight bits to the right and
a = a << 8;
shifts the contents eight bits to the left.
If the data type being shifted is a signed 32 or 64bit type then an arithmetic right shift is performed – i.e. new high order bits are set to the sign bit.
If the data type is an unsigned 32 or 64bit type then a logical right shift is performed. If you want to perform logical shift on a signed type you can simply use an explicit cast.
For example:
int a = 1; int b = a >> 8;
performs an arithmetic shift right but:
int c =(int) ((uint) a >> 8);
performs a logical shift right, i.e. it shifts in zeros from the right.
All of the logical operators and the shifts work with 32 or 64bit data types. In the case of the simple logical operations 16 and 8bit data is automatically converted to 32bit types and you need to use an explicit cast to recover the original data type.
For example:
byte a = 0xFF,b=0xF0; byte c=(byte)(a & b);
Things are just as simple with shifts. For example, consider:
Int16 a = 0xFF; Int16 c=(Int16)((int)a << 8);
You will discover that the answer is 0xFF00.
There is also a problem with signed types. If you attempt to assign a hex value that represents a negative number, for example:
Int16 a = (Int16)0xFFFF;
you’ll find it doesn’t work. If you want to force it to work then use:
Int16 a; unchecked { a = (Int16)0xFFFF; }
where the variable declaration has now to be separate otherwise it would only be defined within the scope of the “unchecked”. If you don’t want this, use unsigned types as in:
UInt16 a = 0xFFFF; UInt16 c = (UInt16)(a << 16);
You could argue that using unsigned types is the best way to represent bit patterns but it is often necessary to perform bit manipulation on other data types simply because this is the object of the exercise.
You can use combinations of shifts and logical operators to create other types of shift including bit rotates. For example, a rotate left routine is fairly easy to create:
UInt16 RotateLeft(UInt16 data, int n) { Int16 mask; unchecked{mask=(Int16)0x8000;} mask= (Int16)(mask >> (n1)); String s = mask.ToString("X"); UInt16 top = (UInt16)(data & mask); top = (UInt16) (top >> (16  n)); data = (UInt16)(data << n); data = (UInt16)(data  top); return data; }
This works by first creating a mask with the top n bits set to one by using an arithmetic shift.
Next we use the mask to extract the top n bits which we shift right to become the bottom n bits. Finally the shift left by n is performed and what were the top n bits are stored as the bottom n bits of the result. Of course this could be written in a more compressed form by combining expressions but the important question of how efficient this is could only be answered by benchmarking against alternative methods such as performing a repeated shift and high to low bit transfer.
<ASIN:1449380344>
<ASIN:0123745144>
<ASIN:0321741765>
<ASIN:0470495995>
