Bytes, bits, hex and number bases

Binary and hex notation, byte ranges, converting between bases by hand and in code, and how to read a hex dump without confusing offsets with data.

Three notations for the same value

A byte is eight bits and holds 256 distinct values. Hex exists because four bits map exactly to one hex digit, so a byte is always exactly two hex characters — no arithmetic required to read it.

DecimalBinaryHexContext
00000 000000Null byte, often a terminator
100000 10100ALine feed
130000 11010DCarriage return
320010 000020Space
650100 000141Uppercase A in ASCII
1270111 11117FDEL, the end of 7-bit ASCII
1281000 000080First value outside ASCII
2551111 1111FFLargest byte value
n = 0x41              # hex literal, value 65
bin(n)                # '0b1000001'
hex(n)                # '0x41'
f"{n:08b}"            # '01000001'  eight bits, zero padded
f"{n:02X}"            # '41'        two hex digits, uppercase

int("ff", 16)         # 255
int("11111111", 2)    # 255

# shifting is how you pack and unpack bytes
high, low = (n >> 4) & 0xF, n & 0xF
packed = (high << 4) | low            # 0x41 again

Reading a hex dump

00000000  50 4b 03 04 14 00 00 00  08 00 21 8a 4c 5d 19 3c  |PK........!.L].<|
00000010  d4 03 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  68 65 6c 6c 6f 2e 74 78  74 0a 77 6f 72 6c 64 0a  |hello.txt.world.|
            ^                                               ^
            byte offset (hex)                               ASCII rendering
  • The left column is the offset of the first byte on the line, in hex — not part of the data.
  • Each pair of hex digits is one byte; the ASCII column on the right is a convenience view.
  • A dot in the ASCII column means the byte is not printable, not that the byte is a dot.
  • 50 4b 03 04 is the ZIP signature PK followed by the local file header marker.
xxd file.bin | head
hexdump -C file.bin | head
xxd -s 0x20 -l 16 file.bin           # 16 bytes starting at offset 32
printf 'hello' | xxd                  # 68656c6c6f

Byte order and where it bites

import struct

value = 0x01020304
struct.pack(">I", value)    # b'\x01\x02\x03\x04'  big endian
struct.pack("<I", value)    # b'\x04\x03\x02\x01'  little endian

# reading a fixed-width field from a binary header
length = struct.unpack_from("<I", data, 8)[0]
💡
Network protocols are big endian by convention, most CPUs are little endian, and some file formats pick either. Always state the endianness when you read or write a multi-byte integer, and never assume the host order.

FAQ

Why do programmers use hex instead of decimal?
Because four bits map to exactly one hex digit, so any byte is two characters. Addresses, colours and magic numbers are far easier to read and byteswap in hex than in decimal.
What is a magic number?
A fixed byte sequence at the start of a file that identifies its format: 50 4b for ZIP, 89 50 4e 47 for PNG, ff d8 ff for JPEG. Tools use it to detect the type regardless of the file extension.

Character sets: ASCII, Latin-1 and Windows-1252 UTF-16, UTF-32, BOM and endianness

Last refreshed 2026-09-18.