Blog
Published On: 2025-07-02 by Wiseman Fernandes
Learn Bitwise Operators

Let's embark on a journey to learn bitwise operations! These are fundamental in many areas of computing, from low-level programming and embedded systems to optimizing algorithms and graphics.

We'll cover the core concepts, operators, common use cases, and provide examples to solidify your understanding.


Understanding the Basics: Bits and Binary


Before diving into operations, it's crucial to understand what bits are and how numbers are represented in binary.

  • Bit: The smallest unit of data in a computer, representing either a 0 or a 1.

  • Binary: A base-2 numbering system, using only the digits 0 and 1. Each position in a binary number represents a power of 2.

Example:The decimal number 5 is represented as 0101 in binary (assuming an 8-bit representation, it's 00000101).

  • 0 * 2^7 + 0 * 2^6 + 0 * 2^5 + 0 * 2^4 + 0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0

  • 0 + 0 + 0 + 0 + 0 + 4 + 0 + 1 = 5


The Bitwise Operators


There are six main bitwise operators in most programming languages (C, C++, Java, JavaScript, Python, etc.). They operate on numbers at the individual bit level.

Let's use two example 8-bit numbers for demonstration:

  • A = 01011010 (decimal 90)

  • B = 00110110 (decimal 54)


1. Bitwise AND (&)


  • Rule: The resulting bit is 1 if both corresponding bits are 1. Otherwise, it's 0.

  • Truth Table:

    • 0 & 0 = 0

    • 0 & 1 = 0

    • 1 & 0 = 0

    • 1 & 1 = 1

  • Example:

     A: 01011010
     B: 00110110
      ----------
    A & B: 00010010  (decimal 18)
    
  • Common Uses:

    • Checking if a specific bit is set: (number & (1 << bitPosition)) != 0

    • Masking (clearing) specific bits: Setting bits to 0. number & ~mask (where ~mask creates a mask with 0s at the positions to clear).

    • Extracting a subset of bits: As seen in this example (0 >> 4 & 15) to extract specific nibbles (4 bits).


2. Bitwise OR (|)


  • Rule: The resulting bit is 1 if at least one of the corresponding bits is 1. Otherwise, it's 0.

  • Truth Table:

    • 0 | 0 = 0

    • 0 | 1 = 1

    • 1 | 0 = 1

    • 1 | 1 = 1

  • Example:

      A: 01011010
      B: 00110110
      ----------
    A | B: 01111110  (decimal 126)
    
  • Common Uses:

    • Setting specific bits: number | (1 << bitPosition)

    • Combining flags/permissions: Each bit represents a different option or permission, and you OR them together.


3. Bitwise XOR (^) (Exclusive OR)


  • Rule: The resulting bit is 1 if the corresponding bits are different. Otherwise, it's 0.

  • Truth Table:

    • 0 ^ 0 = 0

    • 0 ^ 1 = 1

    • 1 ^ 0 = 1

    • 1 ^ 1 = 0

  • Example:

      A: 01011010
      B: 00110110
      ----------
    A ^ B: 01101100  (decimal 108)
    
  • Common Uses:

    • Toggling (flipping) specific bits: number ^ (1 << bitPosition)

    • Swapping two numbers without a temporary variable:

      a = a ^ b;
      b = a ^ b; // (a^b)^b = a
      a = a ^ b; // (a^b)^a = b (original a)
      
    • Detecting differences between two values.

    • Simple encryption/checksums.


4. Bitwise NOT (~) (One's Complement)


  • Rule: Flips every bit. 0 becomes 1, and 1 becomes 0.

  • Example:

      A: 01011010
      ---------
    ~A: 10100101
    
  • Important Note on Signed Numbers: For signed numbers, the most significant bit (leftmost) indicates the sign (0 for positive, 1 for negative). Applying ~ to a positive number usually results in a negative number, due to two's complement representation.

    • In 8-bit, ~0 = -1, ~1 = -2, ~2 = -3, etc. This is because ~x often results in -(x + 1).

  • Common Uses:

    • Creating masks: ~0 creates a mask of all 1s. ~mask inverts a mask.

    • Used with other operations for clearing or setting specific bits.


5. Left Shift (<<)


  • Rule: Shifts all bits to the left by a specified number of positions. New bits on the right are filled with 0s. Bits shifted off the left are discarded.

  • Mathematical Equivalence: Shifting left by n positions is equivalent to multiplying by (as long as there's no overflow).

  • Example:

      A: 01011010 (decimal 90)
    A << 1: 10110100 (decimal 180) - Multiplied by 2
    A << 2: 01101000 (decimal 104) - Multiplied by 4 (with overflow, as 90 * 4 = 360, but 8-bit max is 255)
    
  • Common Uses:

    • Multiplying by powers of 2 efficiently.

    • Creating bit masks dynamically: 1 << n creates a mask with a single 1 at the n-th position (0-indexed).

    • Preparing data for other bitwise operations.


6. Right Shift (>>) (Signed Right Shift / Arithmetic Right Shift)


  • Rule: Shifts all bits to the right by a specified number of positions. Bits shifted off the right are discarded. The behaviour of the bits shifted in on the left depends on whether the number is signed or unsigned.

    • Signed Right Shift (Arithmetic Right Shift): The most significant bit (sign bit) is copied into the new positions on the left. This preserves the sign of the number. (Commonly >> in C/C++/Java/JavaScript).

    • Unsigned Right Shift (Logical Right Shift): New bits on the left are always filled with 0s, regardless of the sign bit. (Commonly >>> in Java/JavaScript, less common in C/C++ without explicit casting to unsigned).

  • Mathematical Equivalence: Shifting right by n positions is equivalent to integer division by .

  • Example (Signed Right Shift >>):

    • Positive Number:

        A: 01011010 (decimal 90)
      A >> 1: 00101101 (decimal 45) - Divided by 2
      
    • Negative Number (e.g., -90 in 8-bit two's complement is 10100110):

        N: 10100110 (decimal -90)
      N >> 1: 11010011 (decimal -45) - Sign bit (1) is replicated
      
  • Example (Unsigned Right Shift >>> in JS/Java):

    • Negative Number:

        N: 10100110 (decimal -90)
      N >>> 1: 01010011 (decimal 83) - Filled with 0s, sign is lost
      
  • Common Uses:

    • Dividing by powers of 2 efficiently.

    • Extracting specific bits or groups of bits: As seen in your example e >> 4.

    • Parsing bit fields from data structures.


Key Concepts and Techniques


Bit Masks


A bit mask is a binary number used in conjunction with bitwise operators to selectively set, clear, or check specific bits in another number.

  • To Check if a bit is set:

    JavaScript


    const number = 0b10101100; // 172
    const bitPosition = 2; // (0-indexed) checking the 3rd bit from right
    const mask = 1 << bitPosition; // 0b00000100 (4)
    
    if ((number & mask) !== 0) {
      console.log(`Bit ${bitPosition} is set.`); // Output: Bit 2 is set.
    } else {
      console.log(`Bit ${bitPosition} is not set.`);
    }
    
  • To Set a bit:

    JavaScript


    let number = 0b10100000; // 160
    const bitToSet = 3;
    const mask = 1 << bitToSet; // 0b00001000 (8)
    
    number = number | mask; // 0b10101000 (168)
    console.log(number.toString(2)); // Output: 10101000
    
  • To Clear a bit:

    JavaScript


    let number = 0b10101000; // 168
    const bitToClear = 3;
    const mask = 1 << bitToClear; // 0b00001000 (8)
    
    number = number & ~mask; // 0b10100000 (160)
    console.log(number.toString(2)); // Output: 10100000
    

    Explanation of ~mask: If mask is 00001000, ~mask in an 8-bit system would be 11110111. When you AND this with number, all bits remain the same except for the 3rd bit, which is forced to 0.

  • To Toggle a bit:

    JavaScript


    let number = 0b10101000; // 168
    const bitToToggle = 3;
    const mask = 1 << bitToToggle; // 0b00001000 (8)
    
    number = number ^ mask; // 0b10100000 (160) - Toggles it off
    console.log(number.toString(2)); // Output: 10100000
    
    number = number ^ mask; // 0b10101000 (168) - Toggles it back on
    console.log(number.toString(2)); // Output: 10101000
    


Practical Applications


  • Graphics and Image Processing: Manipulating individual pixel components (RGBA values), color transformations.

  • Networking: Parsing network packets, setting and checking flags in headers.

  • Embedded Systems/Microcontrollers: Controlling hardware registers, reading sensor data, managing I/O pins.

  • Game Development: Collision detection (using bitmasks for shapes), optimizing game states.

  • Cryptography: Many cryptographic algorithms rely heavily on bitwise operations.

  • Data Compression: Encoding/decoding data at the bit level.

  • Optimizing Performance: Bitwise operations are often much faster than their arithmetic counterparts (multiplication/division by powers of 2).


Tips for Mastering Bitwise Operations


  1. Think in Binary: Always visualize the numbers in their binary representation when working with bitwise operators. Draw them out if needed.

  2. Practice, Practice, Practice: The more you use them, the more intuitive they become. Start with simple problems and gradually increase complexity.

  3. Understand Number Representation: Be aware of how signed and unsigned numbers are represented (two's complement is standard for signed integers). This is crucial for understanding ~ and >>.

  4. Know Your Language's Specifics: While the core operators are universal, some languages might have subtle differences (e.g., >>> in JS/Java).

  5. Use Tools: Online binary converters or programming language debuggers that show binary representations can be very helpful.


Let's Do Some Exercises!


To test your understanding, try to solve these (mentally or with pen and paper first, then use code to verify):

  1. What is 0b11001010 & 0b00111100?

  2. What is 0b10101010 | 0b01010101?

  3. What is 0b11110000 ^ 0b00110011?

  4. If num = 0b00110101, what is num << 3?

  5. If num = 0b10000000 (8-bit, signed), what is num >> 1?

  6. How would you write a function to check if a number is a power of 2 using bitwise operations? (Hint: Consider n & (n - 1))

  7. How would you clear the most significant bit (MSB) of an 8-bit number num?

Once you've tried these, feel free to share your answers, and we can discuss them further! Mastering bitwise operations will open up a new level of understanding and control in your programming journey.

BitwiseOperators