Introduction to Bits and Bytes
Bits and bytes form the foundation of all digital computing. Understanding how data is represented and manipulated at the bit level is crucial for systems programming, embedded development, data compression, cryptography, and performance optimization. This comprehensive guide explores the fundamental concepts of bits and bytes and their practical applications in programming.
Key Concepts
- Bit: The smallest unit of data (0 or 1)
- Byte: Group of 8 bits, the basic addressable unit in most systems
- Binary: Base-2 number system used by computers
- Endianness: Byte ordering in memory
- Bitwise Operations: Direct manipulation of bits
- Data Representation: How different data types are stored
1. Understanding Bits
What is a Bit?
A bit (binary digit) is the most basic unit of information in computing, representing either 0 or 1. All digital data—numbers, text, images, sound—is ultimately stored and processed as sequences of bits.
# A single bit can represent two states
state = 1 # True, ON, HIGH
state = 0 # False, OFF, LOW
# Multiple bits can represent more states
# 1 bit: 2 states (0, 1)
# 2 bits: 4 states (00, 01, 10, 11)
# n bits: 2^n states
# Demonstrating bit combinations
for i in range(8):
print(f"3 bits: {i:03b} represents {i}")
// C representation of bits
#include <stdio.h>
#include <stdint.h>
void print_bits(uint8_t n) {
for (int i = 7; i >= 0; i--) {
printf("%d", (n >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf("\n");
}
int main() {
uint8_t a = 0b10101010; // Binary literal
uint8_t b = 0xAA; // Hex literal (same value)
printf("Binary: "); print_bits(a);
printf("Decimal: %d\n", a);
printf("Hex: 0x%02X\n", a);
return 0;
}
Bit Position and Weight
Each bit in a number has a positional weight based on powers of 2.
# Understanding bit positions and weights
# For an 8-bit number (byte): positions 7 down to 0
def explain_bits(num):
print(f"Number: {num} = {num:08b}")
print("Bit positions (from left to right):")
for i in range(7, -1, -1):
bit = (num >> i) & 1
weight = 2 ** i
print(f" Bit {i}: {bit} (weight {weight}){' * ' + str(bit) if bit else ''}")
total = 0
for i in range(7, -1, -1):
if (num >> i) & 1:
total += 2 ** i
print(f"Total: {total}")
explain_bits(42)
2. Bytes and Word Sizes
The Byte (8 Bits)
A byte is the fundamental addressable unit in most computer architectures. It consists of 8 bits and can represent 256 distinct values (0-255 for unsigned, -128 to 127 for signed).
# Byte ranges
print("Unsigned byte: 0 to 255")
print("Signed byte: -128 to 127")
print("Maximum values:")
print(f" 2^8 = 256 values")
print(f" Max unsigned: {2**8 - 1}")
# Byte representation
def show_byte(n):
if n < 0 or n > 255:
print(f"{n} is outside byte range")
return
print(f"Decimal: {n}")
print(f"Binary: {n:08b}")
print(f"Hex: 0x{n:02X}")
print(f"Octal: 0o{n:03o}")
show_byte(42)
Word Sizes
Different architectures use different word sizes, affecting how data is stored and processed.
import sys
# Common word sizes
word_sizes = {
"8-bit": 1, # 1 byte (8 bits)
"16-bit": 2, # 2 bytes (16 bits)
"32-bit": 4, # 4 bytes (32 bits)
"64-bit": 8, # 8 bytes (64 bits)
}
print("Common word sizes:")
for name, bytes in word_sizes.items():
bits = bytes * 8
print(f" {name}: {bytes} bytes ({bits} bits)")
print(f" Range: 0 to {2**bits - 1} (unsigned)")
print(f" Range: -{2**(bits-1)} to {2**(bits-1)-1} (signed)")
# Check system word size
print(f"\nSystem word size: {sys.maxsize.bit_length()} bits")
Memory Addressing
// C memory addressing example
#include <stdio.h>
#include <stdint.h>
int main() {
int32_t num = 0x01020304;
uint8_t* bytes = (uint8_t*)#
printf("Integer value: 0x%08X\n", num);
printf("Byte addresses (memory view):\n");
for (int i = 0; i < 4; i++) {
printf(" Byte %d at address %p: 0x%02X\n",
i, (void*)&bytes[i], bytes[i]);
}
return 0;
}
3. Binary Number System
Binary to Decimal Conversion
def binary_to_decimal(binary_str):
"""Convert binary string to decimal"""
decimal = 0
for i, bit in enumerate(reversed(binary_str)):
if bit == '1':
decimal += 2 ** i
return decimal
def decimal_to_binary(n, bits=8):
"""Convert decimal to binary string"""
if n < 0:
# Two's complement for negative numbers
n = (1 << bits) + n
return format(n, f'0{bits}b')
# Examples
print("Binary to Decimal:")
print(f" 1010 -> {binary_to_decimal('1010')}")
print(f" 1111 -> {binary_to_decimal('1111')}")
print(f" 10000000 -> {binary_to_decimal('10000000')}")
print("\nDecimal to Binary (8-bit):")
print(f" 42 -> {decimal_to_binary(42)}")
print(f" 255 -> {decimal_to_binary(255)}")
print(f" -1 -> {decimal_to_binary(-1, 8)} (two's complement)")
Hexadecimal and Octal
# Different number bases
num = 42
print(f"Decimal: {num}")
print(f"Binary: {bin(num)}")
print(f"Octal: {oct(num)}")
print(f"Hex: {hex(num)}")
# Parsing different bases
print("\nParsing from strings:")
print(f"int('2A', 16) = {int('2A', 16)}")
print(f"int('101010', 2) = {int('101010', 2)}")
print(f"int('52', 8) = {int('52', 8)}")
# Relationship between bases
print("\nBase relationships:")
print(f"0x2A = 0b101010 = 0o52 = 42")
4. Data Representation
Integer Representation
# Unsigned integers
def unsigned_range(bits):
return f"0 to {2**bits - 1}"
# Signed integers (two's complement)
def signed_range(bits):
return f"-{2**(bits-1)} to {2**(bits-1)-1}"
print("Integer ranges by size:")
for bits in [8, 16, 32, 64]:
print(f"{bits}-bit:")
print(f" Unsigned: {unsigned_range(bits)}")
print(f" Signed: {signed_range(bits)}")
# Two's complement demonstration
def twos_complement(n, bits=8):
"""Convert to two's complement representation"""
if n >= 0:
return n
return (1 << bits) + n
def from_twos_complement(n, bits=8):
"""Convert from two's complement"""
if n < (1 << (bits - 1)):
return n
return n - (1 << bits)
# Example
print("\nTwo's complement examples (8-bit):")
values = [42, -42, 127, -128, 0, -1]
for v in values:
tc = twos_complement(v, 8)
print(f"{v:4d} -> {tc:3d} -> {tc:08b} -> {from_twos_complement(tc):4d}")
Floating Point Representation
import struct
import math
def float_to_bits(f):
"""Convert float to its binary representation"""
return format(struct.unpack('>I', struct.pack('>f', f))[0], '032b')
def double_to_bits(d):
"""Convert double to its binary representation"""
return format(struct.unpack('>Q', struct.pack('>d', d))[0], '064b')
def explain_float(f):
"""Explain IEEE 754 single precision representation"""
bits = float_to_bits(f)
sign = bits[0]
exponent = int(bits[1:9], 2)
mantissa = bits[9:]
print(f"Float: {f}")
print(f"Bits: {bits}")
print(f" Sign: {sign} ({'negative' if sign == '1' else 'positive'})")
print(f" Exponent: {exponent} (bias 127, actual: {exponent - 127})")
print(f" Mantissa: {mantissa}")
# Reconstruct value
if exponent == 0:
# Denormalized or zero
value = (-1)**int(sign) * int(mantissa, 2) * 2**(1-127) / 2**23
elif exponent == 255:
if mantissa == '0' * 23:
value = float('inf') if sign == '0' else float('-inf')
else:
value = float('nan')
else:
# Normalized
value = (-1)**int(sign) * (1 + int(mantissa, 2) / 2**23) * 2**(exponent - 127)
print(f" Value: {value}")
# Examples
explain_float(3.14159)
print()
explain_float(0.1)
print()
explain_float(float('inf'))
Character Representation (ASCII and Unicode)
# ASCII (7-bit)
def ascii_table():
print("ASCII Table (32-126):")
for i in range(32, 127, 16):
row = []
for j in range(16):
if i + j < 127:
char = chr(i + j)
if char.isprintable():
row.append(f"{i+j:3d}: {char}")
else:
row.append(f"{i+j:3d}: ")
print(" ".join(row))
# Character to bytes
def char_info(c):
print(f"Character: '{c}'")
print(f" ASCII/Unicode code point: {ord(c)}")
print(f" Hex: 0x{ord(c):04X}")
print(f" Binary: {ord(c):016b}")
print(f" UTF-8 bytes: {c.encode('utf-8')}")
# Examples
print("Character encoding examples:")
char_info('A')
print()
char_info('€')
print()
char_info('世')
5. Endianness
Little Endian vs Big Endian
import sys
import struct
def check_endianness():
"""Check system endianness"""
if sys.byteorder == 'little':
return "Little Endian (least significant byte first)"
else:
return "Big Endian (most significant byte first)"
print(f"System is: {check_endianness()}")
# Visualize endianness
def show_endianness(num, width=4):
"""Show byte order for a number"""
print(f"Number: 0x{num:08X}")
# Big endian representation
big_endian = num.to_bytes(width, 'big')
print(f"Big endian bytes: {' '.join(f'{b:02X}' for b in big_endian)}")
# Little endian representation
little_endian = num.to_bytes(width, 'little')
print(f"Little endian bytes:{' '.join(f'{b:02X}' for b in little_endian)}")
# Memory layout
print("\nMemory layout (low to high addresses):")
print(" Big endian: ", end="")
for b in big_endian:
print(f"[{b:02X}]", end=" ")
print()
print(" Little endian: ", end="")
for b in little_endian:
print(f"[{b:02X}]", end=" ")
print()
show_endianness(0x01020304)
# Practical example: reading binary data
def read_int_little_endian(data):
return int.from_bytes(data, 'little')
def read_int_big_endian(data):
return int.from_bytes(data, 'big')
# Example
data = bytes([0x04, 0x03, 0x02, 0x01])
print(f"\nData bytes: {' '.join(f'{b:02X}' for b in data)}")
print(f"As little endian: {read_int_little_endian(data):08X}")
print(f"As big endian: {read_int_big_endian(data):08X}")
6. Bit Manipulation Techniques
Setting and Clearing Bits
def set_bit(num, position):
"""Set bit at position to 1"""
return num | (1 << position)
def clear_bit(num, position):
"""Set bit at position to 0"""
return num & ~(1 << position)
def toggle_bit(num, position):
"""Flip bit at position"""
return num ^ (1 << position)
def check_bit(num, position):
"""Check if bit at position is 1"""
return (num >> position) & 1
# Demonstration
num = 0b10101010
print(f"Original: {num:08b}")
# Set bit 1
num = set_bit(num, 1)
print(f"Set bit 1: {num:08b}")
# Clear bit 3
num = clear_bit(num, 3)
print(f"Clear bit 3: {num:08b}")
# Toggle bit 5
num = toggle_bit(num, 5)
print(f"Toggle bit 5: {num:08b}")
print(f"Bit 2: {check_bit(num, 2)}")
print(f"Bit 5: {check_bit(num, 5)}")
Bit Fields
class BitField:
"""Manipulate bit fields within integers"""
def __init__(self, fields):
self.fields = fields # List of (name, width)
self._compute_layout()
def _compute_layout(self):
self.offsets = {}
self.masks = {}
offset = 0
for name, width in self.fields:
self.offsets[name] = offset
self.masks[name] = (1 << width) - 1
offset += width
self.total_bits = offset
def pack(self, **values):
result = 0
for name, value in values.items():
offset = self.offsets[name]
mask = self.masks[name]
result |= (value & mask) << offset
return result
def unpack(self, num):
result = {}
for name, offset in self.offsets.items():
mask = self.masks[name]
result[name] = (num >> offset) & mask
return result
# Example: RGB color with 5-6-5 bits
rgb565 = BitField([('red', 5), ('green', 6), ('blue', 5)])
color = rgb565.pack(red=31, green=63, blue=31) # Maximum values
print(f"RGB565 packed: 0x{color:04X}")
print(f"Unpacked: {rgb565.unpack(color)}")
Bit Masks
class BitMask:
"""Common bit mask operations"""
@staticmethod
def mask_low(n):
"""Create mask with lowest n bits set"""
return (1 << n) - 1
@staticmethod
def mask_range(start, end):
"""Create mask for bits start..end (inclusive)"""
width = end - start + 1
return ((1 << width) - 1) << start
@staticmethod
def extract_range(num, start, end):
"""Extract bits from start to end"""
mask = BitMask.mask_range(start, end)
return (num & mask) >> start
@staticmethod
def is_power_of_two(n):
"""Check if number is power of two"""
return n and (n & (n - 1)) == 0
@staticmethod
def lowest_set_bit(n):
"""Return the lowest set bit"""
return n & -n
@staticmethod
def clear_lowest_set_bit(n):
"""Clear the lowest set bit"""
return n & (n - 1)
# Examples
print("BitMask utilities:")
print(f"Low 4 bits mask: {BitMask.mask_low(4):08b}")
print(f"Range 2-5 mask: {BitMask.mask_range(2, 5):08b}")
num = 0b11011010
print(f"Number: {num:08b}")
print(f"Bits 2-4: {BitMask.extract_range(num, 2, 4):03b}")
print(f"16 is power of two: {BitMask.is_power_of_two(16)}")
print(f"18 is power of two: {BitMask.is_power_of_two(18)}")
print(f"Lowest set bit of {num:08b}: {BitMask.lowest_set_bit(num):08b}")
7. Practical Applications
Bit Packing for Data Compression
class BitPacker:
"""Pack multiple values into a single integer"""
def __init__(self):
self.buffer = 0
self.bit_pos = 0
def pack(self, value, num_bits):
"""Pack value into buffer using num_bits bits"""
if num_bits <= 0:
return
# Ensure value fits in num_bits
value &= (1 << num_bits) - 1
self.buffer |= value << self.bit_pos
self.bit_pos += num_bits
def get_buffer(self):
return self.buffer
def reset(self):
self.buffer = 0
self.bit_pos = 0
class BitUnpacker:
"""Unpack bits from integer"""
def __init__(self, data):
self.data = data
self.bit_pos = 0
def unpack(self, num_bits):
"""Extract num_bits from buffer"""
if num_bits <= 0:
return 0
mask = (1 << num_bits) - 1
value = (self.data >> self.bit_pos) & mask
self.bit_pos += num_bits
return value
# Example: Pack RGB values into 16 bits
packer = BitPacker()
packer.pack(31, 5) # Red (5 bits)
packer.pack(63, 6) # Green (6 bits)
packer.pack(31, 5) # Blue (5 bits)
packed = packer.get_buffer()
print(f"Packed RGB: 0x{packed:04X}")
unpacker = BitUnpacker(packed)
red = unpacker.unpack(5)
green = unpacker.unpack(6)
blue = unpacker.unpack(5)
print(f"Unpacked: R={red}, G={green}, B={blue}")
Error Detection: Parity and Checksums
def parity_bit(data):
"""Calculate even parity bit"""
# XOR all bits together
parity = 0
while data:
parity ^= data & 1
data >>= 1
return parity
def add_parity(data):
"""Add parity bit to data (as LSB)"""
parity = parity_bit(data)
return (data << 1) | parity
def check_parity(data_with_parity):
"""Check parity of data (including parity bit)"""
return parity_bit(data_with_parity) == 0
def xor_checksum(data_bytes):
"""Simple XOR checksum"""
checksum = 0
for b in data_bytes:
checksum ^= b
return checksum
def add_checksum(data_bytes):
"""Add checksum to data"""
checksum = xor_checksum(data_bytes)
return data_bytes + bytes([checksum])
def verify_checksum(data_with_checksum):
"""Verify checksum"""
if len(data_with_checksum) == 0:
return False
data = data_with_checksum[:-1]
checksum = data_with_checksum[-1]
return xor_checksum(data) == checksum
# Example: Parity
original = 0b10110110
print(f"Original: {original:08b}")
with_parity = add_parity(original)
print(f"With parity: {with_parity:09b}")
print(f"Check: {check_parity(with_parity)}")
# Example: Checksum
data = bytes([0x01, 0x02, 0x03, 0x04])
print(f"Data: {' '.join(f'{b:02X}' for b in data)}")
with_cs = add_checksum(data)
print(f"With checksum: {' '.join(f'{b:02X}' for b in with_cs)}")
print(f"Verify: {verify_checksum(with_cs)}")
Bitwise Graphics and Sprites
class Sprite:
"""Simple sprite representation using bits"""
def __init__(self, width, height, data):
self.width = width
self.height = height
self.data = data
@classmethod
def from_bitmap(cls, bitmap_strings):
"""Create sprite from ASCII bitmap"""
height = len(bitmap_strings)
width = max(len(s) for s in bitmap_strings)
data = []
for row in bitmap_strings:
row_val = 0
for i, ch in enumerate(row):
if ch == '1' or ch == '#':
row_val |= 1 << (width - 1 - i)
data.append(row_val)
return cls(width, height, data)
def render(self):
"""Render sprite to console"""
for row in self.data:
line = []
for i in range(self.width):
if (row >> (self.width - 1 - i)) & 1:
line.append('█')
else:
line.append(' ')
print(''.join(line))
# Create a simple smiley face sprite
smiley = Sprite.from_bitmap([
"01111110",
"10000001",
"10100101",
"10000001",
"01011010",
"00100100",
"00011000",
"00000000",
])
print("Sprite representation:")
smiley.render()
# 8x8 heart sprite
heart = Sprite.from_bitmap([
"00111100",
"01111110",
"11111111",
"11111111",
"11111111",
"01111110",
"00111100",
"00011000",
])
print("\nHeart sprite:")
heart.render()
Networking: IP Address Manipulation
class IPAddress:
"""IP address manipulation using bit operations"""
def __init__(self, address):
if isinstance(address, str):
self.ip_int = self.ip_to_int(address)
else:
self.ip_int = address
@staticmethod
def ip_to_int(ip_str):
"""Convert IP string to integer"""
parts = ip_str.split('.')
return (int(parts[0]) << 24) | (int(parts[1]) << 16) | \
(int(parts[2]) << 8) | int(parts[3])
@staticmethod
def int_to_ip(ip_int):
"""Convert integer to IP string"""
return f"{(ip_int >> 24) & 0xFF}.{(ip_int >> 16) & 0xFF}." \
f"{(ip_int >> 8) & 0xFF}.{ip_int & 0xFF}"
def network_address(self, netmask):
"""Calculate network address"""
return IPAddress(self.ip_int & netmask.ip_int)
def broadcast_address(self, netmask):
"""Calculate broadcast address"""
return IPAddress(self.ip_int | (~netmask.ip_int & 0xFFFFFFFF))
def __str__(self):
return self.int_to_ip(self.ip_int)
class NetMask:
"""Network mask manipulation"""
def __init__(self, cidr):
self.cidr = cidr
self.mask_int = 0xFFFFFFFF << (32 - cidr) & 0xFFFFFFFF
def __str__(self):
return IPAddress.int_to_ip(self.mask_int)
def ip(self):
return IPAddress(self.mask_int)
# Example
ip = IPAddress("192.168.1.100")
netmask = NetMask(24) # 255.255.255.0
print(f"IP Address: {ip}")
print(f"Netmask: {netmask}")
print(f"Network: {ip.network_address(netmask.ip())}")
print(f"Broadcast: {ip.broadcast_address(netmask.ip())}")
# Calculate subnet details
host_bits = 32 - netmask.cidr
num_hosts = (1 << host_bits) - 2
print(f"Number of usable hosts: {num_hosts}")
# Calculate IP ranges
network = ip.network_address(netmask.ip())
first_host = IPAddress(network.ip_int + 1)
last_host = IPAddress(ip.broadcast_address(netmask.ip()).ip_int - 1)
print(f"Usable range: {first_host} - {last_host}")
8. Performance Considerations
Bit Operations vs Arithmetic
import timeit
def test_performance():
"""Compare bit operations vs arithmetic"""
n = 1000000
def bit_multiply():
x = 42
for _ in range(n):
_ = x << 3
def arith_multiply():
x = 42
for _ in range(n):
_ = x * 8
def bit_divide():
x = 42
for _ in range(n):
_ = x >> 3
def arith_divide():
x = 42
for _ in range(n):
_ = x // 8
print("Performance comparison (1,000,000 iterations):")
time_bit_mul = timeit.timeit(bit_multiply, number=1)
time_arith_mul = timeit.timeit(arith_multiply, number=1)
print(f"Bit shift multiply: {time_bit_mul:.4f}s")
print(f"Arithmetic multiply: {time_arith_mul:.4f}s")
time_bit_div = timeit.timeit(bit_divide, number=1)
time_arith_div = timeit.timeit(arith_divide, number=1)
print(f"Bit shift divide: {time_bit_div:.4f}s")
print(f"Arithmetic divide: {time_arith_div:.4f}s")
test_performance()
Memory Efficiency with Bit Fields
import sys
class BitFlags:
"""Store flags using bits instead of booleans"""
def __init__(self):
self._flags = 0
def set_flag(self, bit):
self._flags |= (1 << bit)
def clear_flag(self, bit):
self._flags &= ~(1 << bit)
def check_flag(self, bit):
return (self._flags >> bit) & 1
def __sizeof__(self):
# Return actual memory usage
return sys.getsizeof(self._flags)
class BooleanFlags:
"""Store flags using separate booleans"""
def __init__(self, num_flags):
self.flags = [False] * num_flags
def set_flag(self, bit):
self.flags[bit] = True
def clear_flag(self, bit):
self.flags[bit] = False
def check_flag(self, bit):
return self.flags[bit]
def __sizeof__(self):
return sys.getsizeof(self.flags) + sum(sys.getsizeof(f) for f in self.flags)
# Memory comparison
num_flags = 64
bit_flags = BitFlags()
bool_flags = BooleanFlags(num_flags)
print(f"Memory for {num_flags} flags:")
print(f" Bit flags: {sys.getsizeof(bit_flags)} bytes")
print(f" Boolean flags: {sys.getsizeof(bool_flags)} bytes")
9. Common Pitfalls
Sign Extension
def sign_extension_demo():
"""Demonstrate sign extension in bit operations"""
# 8-bit signed number: -42
signed_byte = -42 & 0xFF # Two's complement: 0xD6
print(f"Signed byte (8-bit): {signed_byte:08b} = {signed_byte}")
# Extending to 16-bit (sign extension)
extended_16 = signed_byte
if signed_byte & 0x80: # Check sign bit
extended_16 |= 0xFF00
print(f"Extended to 16-bit: {extended_16:016b} = {extended_16}")
print(f"Interpreted as signed: {extended_16 - (1 << 16)}")
sign_extension_demo()
Integer Overflow
def overflow_demo():
"""Demonstrate overflow behavior in different languages"""
# Python handles large integers automatically
x = 2**63 - 1
print(f"Python: {x} + 1 = {x + 1}") # No overflow
# Simulating 32-bit overflow
def add_32bit(a, b):
result = a + b
return result & 0xFFFFFFFF # Mask to 32 bits
x = 0xFFFFFFFF
print(f"32-bit: {x:08X} + 1 = {add_32bit(x, 1):08X}")
overflow_demo()
Shift Count Issues
def shift_issues():
"""Demonstrate shift count issues"""
num = 0b1
# Shifting by more than bit width
print(f"Shifting by 32 on 32-bit value: {num << 32}")
print(f"In many languages, shifting by 32 on 32-bit value wraps to 0")
# Negative shifts
# Most languages don't allow negative shifts
try:
result = num << -1
except ValueError as e:
print(f"Negative shift error: {e}")
shift_issues()
10. Real-World Examples
File Format Parsing (PNG Header)
def parse_png_header(data):
"""Parse PNG file header"""
if len(data) < 8:
return None
# PNG signature: 8 bytes
signature = data[:8]
expected = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
if signature != expected:
return None
# IHDR chunk
chunk_length = int.from_bytes(data[8:12], 'big')
chunk_type = data[12:16].decode('ascii')
if chunk_type != 'IHDR':
return None
# Parse IHDR data
width = int.from_bytes(data[16:20], 'big')
height = int.from_bytes(data[20:24], 'big')
bit_depth = data[24]
color_type = data[25]
compression = data[26]
filter_method = data[27]
interlace = data[28]
return {
'width': width,
'height': height,
'bit_depth': bit_depth,
'color_type': color_type,
'compression': compression,
'filter_method': filter_method,
'interlace': interlace,
}
# Example with test data
import struct
def create_test_png_header(width, height):
"""Create minimal PNG IHDR chunk"""
png_signature = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
chunk_length = struct.pack('>I', 13) # IHDR is always 13 bytes
chunk_type = b'IHDR'
data = struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0)
crc = 0 # Would need to calculate actual CRC
return png_signature + chunk_length + chunk_type + data + struct.pack('>I', crc)
# Create test header
test_header = create_test_png_header(800, 600)
result = parse_png_header(test_header)
print("PNG Header parsed:")
for key, value in result.items():
print(f" {key}: {value}")
Random Number Generation
class XorshiftRNG:
"""Xorshift random number generator using bit operations"""
def __init__(self, seed):
self.state = seed
def next(self):
"""Generate next random number"""
x = self.state
x ^= (x << 13) & 0xFFFFFFFFFFFFFFFF
x ^= (x >> 7) & 0xFFFFFFFFFFFFFFFF
x ^= (x << 17) & 0xFFFFFFFFFFFFFFFF
self.state = x
return x
def next_int(self, max_val):
"""Generate random integer in [0, max_val)"""
return self.next() % max_val
def next_float(self):
"""Generate random float in [0, 1)"""
return self.next() / (1 << 64)
# Test the generator
rng = XorshiftRNG(12345)
print("Xorshift random numbers:")
for i in range(5):
print(f" {rng.next():016X}")
print("\nUniform integers (0-99):")
for i in range(10):
print(f" {rng.next_int(100)}", end=" ")
print()
Binary File Format (Custom)
class BinaryFileFormat:
"""Simple binary file format with header and data"""
MAGIC = 0x42494E46 # "BINF" in hex
VERSION = 1
@staticmethod
def write(filename, data, metadata=None):
"""Write data to binary file"""
with open(filename, 'wb') as f:
# Write header
f.write(struct.pack('>I', BinaryFileFormat.MAGIC))
f.write(struct.pack('>H', BinaryFileFormat.VERSION))
f.write(struct.pack('>I', len(data)))
# Write metadata
if metadata:
meta_bytes = str(metadata).encode()
f.write(struct.pack('>H', len(meta_bytes)))
f.write(meta_bytes)
else:
f.write(struct.pack('>H', 0))
# Write data
f.write(data)
@staticmethod
def read(filename):
"""Read data from binary file"""
with open(filename, 'rb') as f:
# Read header
magic = struct.unpack('>I', f.read(4))[0]
if magic != BinaryFileFormat.MAGIC:
raise ValueError("Invalid file format")
version = struct.unpack('>H', f.read(2))[0]
data_len = struct.unpack('>I', f.read(4))[0]
meta_len = struct.unpack('>H', f.read(2))[0]
# Read metadata
if meta_len > 0:
meta_bytes = f.read(meta_len)
metadata = eval(meta_bytes.decode())
else:
metadata = None
# Read data
data = f.read(data_len)
return {
'version': version,
'metadata': metadata,
'data': data
}
# Example usage
import tempfile
# Write binary file
with tempfile.NamedTemporaryFile(delete=False) as tmp:
filename = tmp.name
# Test data
test_data = b"Hello, World!"
test_metadata = {"author": "User", "timestamp": "2024-01-01"}
BinaryFileFormat.write(filename, test_data, test_metadata)
result = BinaryFileFormat.read(filename)
print(f"Read from file: {result['data']}")
print(f"Metadata: {result['metadata']}")
print(f"Version: {result['version']}")
Conclusion
Bits and bytes form the foundation of all digital computing:
Key Takeaways
- Bits are the smallest unit of digital information (0 or 1)
- Bytes are 8 bits and the basic addressable unit in most systems
- Data representation affects how values are stored and interpreted
- Endianness matters when exchanging data between systems
- Bit operations enable efficient, low-level programming
- Understanding bits is essential for systems programming, networking, and optimization
Quick Reference
| Term | Meaning | Example |
|---|---|---|
| Bit | Binary digit (0 or 1) | Single state |
| Byte | 8 bits | 0x00 to 0xFF |
| Nibble | 4 bits | 0x0 to 0xF |
| Word | 2-8 bytes (depends on architecture) | 32-bit: 4 bytes |
| LSB | Least Significant Byte | Lowest address in little-endian |
| MSB | Most Significant Byte | Highest address in little-endian |
Common Bit Values
| Value | Binary | Hex | Use |
|---|---|---|---|
| 1 | 00000001 | 0x01 | Single bit |
| 2 | 00000010 | 0x02 | Second bit |
| 4 | 00000100 | 0x04 | Third bit |
| 8 | 00001000 | 0x08 | Fourth bit |
| 16 | 00010000 | 0x10 | Fifth bit |
| 32 | 00100000 | 0x20 | Sixth bit |
| 64 | 01000000 | 0x40 | Seventh bit |
| 128 | 10000000 | 0x80 | Eighth bit |
| 255 | 11111111 | 0xFF | All bits set |
Understanding bits and bytes is fundamental to computer science and programming. Whether you're working with low-level hardware, optimizing performance, or simply understanding how data is stored, this knowledge is invaluable!