Base16 explained

Computers work in binary. Humans can't. A byte holds 256 values, and writing them as ones and zeros gets unreadable fast -- 11111111 is just 255. Good luck spotting an off-by-one error in a wall of those.

Hexadecimal (base-16) fixes this by mapping each group of four bits to one character. Four bits can represent 16 values, so you need 16 symbols: 0-9 for zero through nine, A-F for ten through fifteen1. One byte always becomes exactly two hex characters. No padding, no variable-length surprises.

Hex digit mapping table showing decimal, binary, and hex equivalents for values 0 through 15The 16 hex digits

How it got here

Before hex, many architectures used octal (base-8). Octal maps three bits to a digit, which works great for 12-bit or 36-bit word lengths -- the PDP-8 crowd loved it. But 8 isn't divisible by 3, so octal is awkward for 8-bit bytes.

The shift happened on April 7, 1964, when IBM announced the System/3602. The S/360 standardized the 8-bit byte as the basic unit of memory, and that decision shaped everything that came after3. Two hex digits per byte, no leftovers -- hex became the obvious notation.

The A-F convention got popularized through IBM's Fortran IV manual for the System/360, published around 19664. Not everyone was thrilled. Bruce Alan Martin of Brookhaven National Laboratory wrote to the Communications of the ACM in 1968, calling A-F "ridiculous" and proposing new symbols that would visually encode binary structure5. Nobody adopted them. We're still using A-F.

The encoding mechanism

Diagram showing how Base16 encodes the letter A into hex digits 41Base16 encoding process

For each byte, split it into two nibbles -- the high 4 bits and low 4 bits. Look up each nibble in the alphabet 0123456789ABCDEF. Done.

The letter "A" (decimal 65, binary 01000001) splits into 0100 (4) and 0001 (1), giving 41. The string "foobar" becomes 666F6F6261721.

Step-by-step nibble split showing byte to high nibble and low nibble to two hex charactersByte-to-hex nibble splitting

Unlike Base64 and Base32, there's no padding. Every byte maps to exactly two characters, every time. The encoding is case-insensitive too -- 4a and 4A are the same byte. RFC 4648 does note that in some security contexts, case differences could theoretically serve as a covert channel1. I've never seen that exploited in practice, but it's a fun thing to know.

RFC 4648

RFC 4648, published in October 2006 by Simon Josefsson, formally standardized Base16 alongside Base32 and Base641. It obsoleted the earlier RFC 3548 from 2003, mostly tightening the language and adding security notes6.

The three encodings form an efficiency spectrum: Base64 encodes 6 bits per character, Base32 encodes 5, Base16 encodes 4. Hex doubles your data size -- the least compact of the three. But it's also the simplest, and when you just need to eyeball some bytes, compactness isn't the point.

Comparison chart of Base16, Base32, and Base64 encoding efficiencyEncoding efficiency comparison

A draft revision (draft-josefsson-rfc4648bis) is in progress as of 2025 to promote the standard to Internet Standard status7.

Hex in the wild

Hex in everyday computing -- CSS colors, MAC addresses, crypto hashes, and memory dumpsWhere hex shows up

CSS colors -- The #RRGGBB format that web developers write daily is three hex-encoded bytes for red, green, and blue. #FF0000 is pure red. The W3C put this into CSS Level 1 back in December 19968, and while CSS has gained oklch() and all sorts of modern color functions since, six-digit hex is probably still the most widely written hex on the planet.

MAC addresses use six hex octets separated by colons: 00:1A:2B:3C:4D:5E. Defined by IEEE 802 and documented in RFC 70429. Twelve hex digits for 48 bits.

Cryptographic hashes -- SHA-256 output is 32 bytes rendered as 64 hex characters10. Most developers handle these hex strings daily without thinking of them as "Base16 encoded data." But that's what they are.

Memory dumps and hex editors -- the convention of 16 bytes per line, hex on the left, ASCII on the right, has been around since the 1960s and nobody's bothered to replace it.

Hex in programming languages

The 0x prefix for hex literals traces back to C. Dennis Ritchie introduced it in the early 1970s at Bell Labs to disambiguate hex from decimal and octal11. From C, it spread to C++, Java, JavaScript, Python, Rust, Go, and practically everything with C-family syntax. Ada does 16#FF#, some assemblers use a trailing h, but 0x won.

String escape sequences are another hex surface. C uses \x41 for a byte, Python adds \u0041 for Unicode, URLs use %20 for a space. Different syntax, same idea -- byte values as hex digit pairs.

LanguageHex literalString escapeNotes
C/C++0xFF\xFFOriginal 0x convention
Java0xFF\u00FFUnicode escapes only
JavaScript0xFF\xFF or \u00FFBoth byte and Unicode escapes
Python0xFF\xFF or \u00FFAlso bytes.hex() and bytes.fromhex()
Rust0xFF\xFFStrict -- only valid byte values in byte strings
Go0xFF\xFFAlso fmt.Sprintf("%x", data)

When to use hex vs. Base64

If a human needs to read the data, use hex. A hex dump is trivially decodable -- you can eyeball pairs and mentally convert. You can't do that with Base64, where 6-bit groupings don't align with byte boundaries.

If a machine transports the data and size matters, use Base64. It's ~33% overhead instead of 100%.

My rule of thumb: hashes, addresses, debug output -- hex. Email attachments, embedded images, JWTs -- Base64. Base32 exists for the middle ground (TOTP codes use it), but I rarely see it anywhere else.

Citations

  1. RFC 4648: The Base16, Base32, and Base64 Data Encodings. S. Josefsson, October 2006 ↩ ↩2 ↩3 ↩4

  2. IBM: The IBM System/360. Retrieved March 16, 2026 ↩

  3. G. M. Amdahl, G. A. Blaauw, F. P. Brooks, Jr.: Architecture of the IBM System/360. IBM Journal of Research and Development, Vol. 8, No. 2, April 1964 ↩

  4. IBM: IBM System/360 Fortran IV Language, 1966. Referenced in historical accounts of hexadecimal notation standardization ↩

  5. Bruce Alan Martin: Letters to the Editor -- On Binary Notation. Communications of the ACM, Vol. 11, No. 10, October 1968 ↩

  6. RFC 3548: The Base16, Base32, and Base64 Data Encodings. S. Josefsson, July 2003 ↩

  7. IETF: draft-josefsson-rfc4648bis -- The Base16, Base32, and Base64 Data Encodings. Retrieved March 16, 2026 ↩

  8. W3C: Cascading Style Sheets, level 1. W3C Recommendation, December 17, 1996. Retrieved March 16, 2026 ↩

  9. RFC 7042: IANA Considerations and IETF Protocol and Documentation Usage for IEEE 802 Parameters. Retrieved March 16, 2026 ↩

  10. NIST: FIPS PUB 180-4 -- Secure Hash Standard (SHS). Retrieved March 16, 2026 ↩

  11. Brian W. Kernighan and Dennis M. Ritchie: The C Programming Language. Prentice Hall, 1978 ↩

Updated: March 16, 2026