Bitwise Operations

Levython provides low-level bitwise operations for efficient bit manipulation and system programming.

Bitwise Operators

Operator Name Example Result
& AND 12 & 10 8
| OR 12 | 10 14
^ XOR 12 ^ 10 6
~ NOT ~12 -13
<< Left shift 5 << 2 20
>> Right shift 20 >> 2 5

Basic Examples

AND Operation

# Bitwise AND
# 12 = 1100
# 10 = 1010
#      ----
#      1000 = 8
result <- 12 & 10
say(result)  # Output: 8

# Check if bit is set
flags <- 0b1101  # Binary literal
if (flags & 0b0100) != 0 then
    say("Bit 2 is set")
end

OR Operation

# Bitwise OR
# 12 = 1100
# 10 = 1010
#      ----
#      1110 = 14
result <- 12 | 10
say(result)  # Output: 14

# Set a bit
flags <- 0b1001
flags <- flags | 0b0100  # Set bit 2
say(flags)  # Output: 13 (0b1101)

XOR Operation

# Bitwise XOR
# 12 = 1100
# 10 = 1010
#      ----
#      0110 = 6
result <- 12 ^ 10
say(result)  # Output: 6

# Toggle a bit
flags <- 0b1101
flags <- flags ^ 0b0100  # Toggle bit 2
say(flags)  # Output: 9 (0b1001)

NOT Operation

# Bitwise NOT (complement)
result <- ~12
say(result)  # Output: -13

# Explanation: ~12 flips all bits
# 12 = 00001100 → 11110011 = -13 (two's complement)

Shift Operations

# Left shift (multiply by 2^n)
result <- 5 << 2  # 5 * 2^2 = 20
say(result)  # Output: 20

# Right shift (divide by 2^n)
result <- 20 >> 2  # 20 / 2^2 = 5
say(result)  # Output: 5

# Efficient power of 2 multiplication
x <- 7
x2 <- x << 1   # x * 2 = 14
x4 <- x << 2   # x * 4 = 28
x8 <- x << 3   # x * 8 = 56

Common Patterns

Bit Masking

# Extract specific bits
value <- 0b11010110
mask <- 0b00001111

lower_bits <- value & mask
say(lower_bits)  # Output: 6 (0b0110)

# Extract bytes from integer
num <- 0x12345678
byte0 <- num & 0xFF          # 0x78
byte1 <- (num >> 8) & 0xFF   # 0x56
byte2 <- (num >> 16) & 0xFF  # 0x34
byte3 <- (num >> 24) & 0xFF  # 0x12

Set/Clear/Toggle Bits

# Set bit at position n
fun set_bit(value, n)
    return value | (1 << n)
end

# Clear bit at position n
fun clear_bit(value, n)
    return value & ~(1 << n)
end

# Toggle bit at position n
fun toggle_bit(value, n)
    return value ^ (1 << n)
end

# Check if bit n is set
fun is_bit_set(value, n)
    return (value & (1 << n)) != 0
end

# Example usage
flags <- 0
flags <- set_bit(flags, 2)    # Set bit 2
flags <- set_bit(flags, 5)    # Set bit 5
say(flags)                     # Output: 36 (0b100100)

flags <- clear_bit(flags, 2)  # Clear bit 2
say(flags)                     # Output: 32 (0b100000)

Swap Without Temporary

# XOR swap trick
a <- 10
b <- 20

say("Before: a=" + str(a) + ", b=" + str(b))

a <- a ^ b
b <- a ^ b
a <- a ^ b

say("After: a=" + str(a) + ", b=" + str(b))
# Output: a=20, b=10

Count Set Bits

# Count number of 1 bits (population count)
fun popcount(n)
    count <- 0
    while n > 0 do
        count <- count + (n & 1)
        n <- n >> 1
    end
    return count
end

say(popcount(0b1011))  # Output: 3
say(popcount(15))      # Output: 4 (0b1111)

Check Power of Two

# Check if number is power of 2
fun is_power_of_two(n)
    return n > 0 and (n & (n - 1)) == 0
end

say(is_power_of_two(16))  # true
say(is_power_of_two(15))  # false
say(is_power_of_two(32))  # true

Practical Applications

Flags and Permissions

# Define permission flags
READ <- 1    # 0b001
WRITE <- 2   # 0b010
EXEC <- 4    # 0b100

# Grant permissions
perms <- READ | WRITE  # 0b011

# Check permission
if (perms & WRITE) != 0 then
    say("Write permission granted")
end

# Add permission
perms <- perms | EXEC  # Now 0b111

# Remove permission
perms <- perms & ~WRITE  # Remove write

Color Manipulation

# RGB color as 24-bit integer (0xRRGGBB)
fun make_color(r, g, b)
    return (r << 16) | (g << 8) | b
end

fun get_red(color)
    return (color >> 16) & 0xFF
end

fun get_green(color)
    return (color >> 8) & 0xFF
end

fun get_blue(color)
    return color & 0xFF
end

# Create color
purple <- make_color(128, 0, 128)
say("Red: " + str(get_red(purple)))      # 128
say("Green: " + str(get_green(purple)))  # 0
say("Blue: " + str(get_blue(purple)))    # 128

Bit Packing

# Pack multiple values into single integer
fun pack(a, b, c, d)
    # Pack 4 bytes into 32-bit integer
    return (a << 24) | (b << 16) | (c << 8) | d
end

fun unpack(value)
    a <- (value >> 24) & 0xFF
    b <- (value >> 16) & 0xFF
    c <- (value >> 8) & 0xFF
    d <- value & 0xFF
    return [a, b, c, d]
end

# Usage
packed <- pack(10, 20, 30, 40)
values <- unpack(packed)
say(values)  # [10, 20, 30, 40]

Performance Tips

  • Bitwise operations are extremely fast (single CPU cycle)
  • Use shifts instead of multiply/divide by powers of 2
  • Bit masks are faster than multiple boolean checks
  • Pack related flags into single integer instead of separate booleans
  • Bitwise AND with power of 2 minus 1 is fast modulo: n & (2^k - 1)

Reference Table

Operation Formula Use Case
Set bit n x | (1 << n) Turn bit on
Clear bit n x & ~(1 << n) Turn bit off
Toggle bit n x ^ (1 << n) Flip bit
Test bit n (x & (1 << n)) != 0 Check if set
Power of 2? (x & (x-1)) == 0 Validation
Multiply by 2^n x << n Fast multiply
Divide by 2^n x >> n Fast divide