Following reading the latest CodeProject newsletter, I came across this article on bitwise operations. It makes for interesting reading, and I can certainly see the benefit of checking if an integer is even or odd, but testing if the n-th bit is set? What can possibly be the advantages of this?
Bitwise operations are absolutely essential when programming hardware registers in embedded systems. For example every processor that I have ever used has one or more registers (usually a specific memory address) that control whether an interrupt is enabled or disabled. To allow an interrupt to fire the usual process is to set the enable bit for that one interrupt type while, most importantly, not modifying any of the other bits in the register.
When an interrupt fires it typically sets a bit in a status register so that a single service routine can determine the precise reason for the interrupt. Testing the individual bits allows for a fast decode of the interrupt source.
In many embedded systems the total RAM available may be 64, 128 or 256 BYTES (that is Bytes not kilobytes or megabytes) In this environment it is common to use one byte to store multiple data items, boolean flags etc. and then use bit operations to set and read these.
I have, for a number of years been working with a satellite communications system where the message payload is 10.5 bytes. To make the best use of this data packet the information must be packed into the data block without leaving any unused bits between the fields. This means making extensive use of bitwise and shift operators to take the information values and pack them into the payload being transmitted.
Basically, you use them due to size and speed considerations. Bitwise operations are incredibly simple and thus usually faster than arithmetic operations. For example to get the green portion of an rgb value, the arithmetic approach is
(rgb / 256) % 256. With bitwise operations you would do something as
(rgb >> 8) & 0xFF. The latter is significantly faster and once you're used to it, its also easier. Generally bitwise operations come into play a lot when you need to encode/decode data in a compact and fast way.
These kind of operations are often used when writing for embedded systems where memory or CPU power is restricted.
For example, to save space you may store multiple variables in a single 8-bit int variable by using each bit to represent a boolean. Then you need a fast way to set a specific bit or retrieve the bit value.
Generally when programming in higher level languages like C# on a desktop PC with Gigabytes of memory, you don't really care that each
bool takes up a whole byte. But if you're programming a microcontroller in C with 2kb of memory, every single bit counts, so the ability to pack 8 bools into a single byte may be critical.
Bitwise operations are also used frequently in video and audio codecs, for the same reason as in embedded electronics; being able to pack five flags and an eleven bit timer into half an int is very useful when you want to make a super-efficient video codec.
In fact, MPEG 4 even uses exponential Golomb encoding for variable-bit-length fields. A value that was 17 or 19 bits wide last packet might be only three or five bits wide this packet - and you'd figure all that out with bitwise operations.
Tricks that combine bitwise logical operations, bitwise shift operations and arithmetic operations can be understood by people who have studied the construction of a binary adder using logic gates (and, or, not). Outside that circle, it is very difficult to understand without a detailed comment.
It is useful when programming SIMD units, especially if the CPU's architecture intentionally left out some SIMD instructions because they could be emulated by a few others.
For example, the architecture may not define any instructions for taking the negative values of a group of 16 bytes, but that can be emulated by bitwise negating and then adding 1. Likewise, subtraction can be omitted too, because it can be emulated by taking the negative of the second operand. The availability of the "alternate route" is the reason for omitting certain instructions.
Likewise, the SIMD may only support parallel 8-bit addition, without implementing addition for wider elements such as 16-bit, 32-bit or 64-bit. To emulate them, one needs to extract the sign bit from the 8-bit calculation result, then perform the carry operation on the next element.