How to Use the Shift Operators << and >> in Go

Use the `<<` operator to shift bits left (multiplying by powers of 2) and `>>` to shift bits right (dividing by powers of 2), keeping in mind that Go shifts are always unsigned for the count and preserve the sign bit for signed integers during right shifts.

Use the << operator to shift bits left (multiplying by powers of 2) and >> to shift bits right (dividing by powers of 2), keeping in mind that Go shifts are always unsigned for the count and preserve the sign bit for signed integers during right shifts. These operators work on all integer types and are essential for low-level bit manipulation, flag management, and efficient arithmetic.

Here is a practical example demonstrating left and right shifts, including how Go handles negative numbers:

package main

import "fmt"

func main() {
    // Left shift: multiplies by 2^n
    // 5 is 0b101; shifting left by 2 results in 0b10100 (20)
    val := 5
    shiftedLeft := val << 2
    fmt.Printf("5 << 2 = %d (binary: %08b)\n", shiftedLeft, shiftedLeft)

    // Right shift: divides by 2^n
    // 20 >> 2 results in 5
    shiftedRight := shiftedLeft >> 2
    fmt.Printf("20 >> 2 = %d (binary: %08b)\n", shiftedRight, shiftedRight)

    // Signed right shift preserves the sign bit (arithmetic shift)
    // -5 is ...11111011; shifting right fills with 1s
    negVal := -5
    negShifted := negVal >> 1
    fmt.Printf("-5 >> 1 = %d (binary: %08b)\n", negShifted, negShifted)

    // Using shifts for bit flags
    const (
        FlagRead  = 1 << iota
        FlagWrite
        FlagExecute
    )
    
    permissions := FlagRead | FlagExecute
    hasRead := (permissions & FlagRead) != 0
    fmt.Printf("Has Read permission: %v\n", hasRead)
}

When using these operators, remember that the right operand (the shift count) is treated as an unsigned integer. If you shift by a count greater than or equal to the width of the type, the result is zero. For example, shifting an int8 (8 bits) by 8 or more yields 0. Unlike some other languages, Go does not perform circular shifts; bits shifted out are discarded, and new bits are filled with zeros (left shift) or the sign bit (right shift on signed types).

A common pitfall is assuming >> performs a logical shift (filling with zeros) on negative numbers. In Go, >> is an arithmetic shift for signed types, meaning it preserves the sign. If you need a logical right shift on a signed integer, you must cast it to an unsigned type first:

x := int8(-1)
// Arithmetic shift: -1 >> 1 = -1
fmt.Println(x >> 1) 

// Logical shift: cast to uint8 first
fmt.Println(uint8(x) >> 1) // Output: 127 (0b01111111)

Use these operators when you need to pack data, manipulate hardware registers, or perform fast multiplication/division by powers of two, but always verify the type width and sign behavior to avoid unexpected results.