Back to the map

Module 01 · Machine basics · Lesson 3 of 4

How data is represented

Bits, bytes and the encodings behind every value: binary and hex, integer sizes and overflow, two's complement, floating point and why 0.1 + 0.2 is not 0.3, text as UTF-8, and byte order. The representation bugs that reach production.

IntroShould-knowJunior 10 min Updated Nov 2026

Why it matters

Some of the most expensive production bugs are representation bugs: an order ID that overflowed a 32-bit integer, money that drifted by a cent because it was a float, a username limit that cut an emoji in half, a timestamp that will wrap in 2038. None needs deep theory to avoid. They need you to know how a value is stored and what its limits are.

The idea

Everything in memory is bits, grouped into 8-bit bytes. A type is an agreement about how to interpret a run of bytes: as an unsigned integer, a signed integer, a floating-point number, or a sequence of characters. The same four bytes mean different things under different agreements, and each agreement has edges.

Binary and hexadecimal

Binary is base 2: 00101010 is 32 + 8 + 2 = 42. Hexadecimal is base 16 and maps exactly four bits to one digit, so a byte is always two hex digits: 42 is 0x2A. Hex is how memory addresses, colours, hashes and byte dumps are written because it is compact and lines up with bytes.

001010100010 → 21010 → A0x2A = 32 + 8 + 2 = 42
One byte, three readings: eight bits, two hex digits, one decimal number. Each hex digit is exactly four bits.

Integers and overflow

TypeBytesRangeRuns out when…
Signed 32-bit int4about −2.1 billion to +2.1 billionA table's auto-increment ID passes 2,147,483,647
Unsigned 32-bit40 to about 4.3 billionIPv4 addresses ran out at this size
Signed 64-bit8about ±9.2 × 10^18Practically never for counters and IDs
32-bit Unix time (seconds)4up to 19 January 2038Systems still storing time in signed 32 bits

Signed integers use two's complement: the top bit carries a negative weight, so adding 1 to the largest positive value wraps to the most negative one rather than failing. Some languages wrap silently, some throw, some treat it as undefined behaviour. The defence is choosing 64-bit types for IDs, counters and timestamps by default, and checking arithmetic on untrusted inputs.

Floating point

Floats (IEEE 754) store a sign, an exponent and a fraction in binary, like scientific notation in base 2. They cover huge ranges with about 7 (32-bit) or 15 to 16 (64-bit) significant decimal digits. The catch: most decimal fractions, including 0.1, have no exact binary representation, so 0.1 + 0.2 yields 0.30000000000000004. Errors accumulate across many operations, and comparing floats with == is unreliable.

Value typeUseAvoid
MoneyInteger minor units (cents) or a decimal type, plus a currency codefloat and double
Measurements, ML, graphicsfloat or doubleExact equality comparisons
IDs and counters64-bit integers, or opaque strings32-bit integers; floats (JavaScript numbers lose precision above 2^53)
Timestamps64-bit epoch milliseconds or an ISO 8601 string in UTC32-bit seconds; local time without a zone

Text

A character set assigns numbers to characters (Unicode has over a million code points); an encoding turns those numbers into bytes. UTF-8, the near-universal default, uses one byte for ASCII and two to four bytes for everything else. So string length in bytes, in code points and in visible characters can all differ: an emoji family can be one visible character, several code points and over twenty bytes. Database column limits, API size limits and truncation must say which they mean, and truncation must never cut in the middle of a character.

Byte order and alignment

Multi-byte numbers can be stored with the most significant byte first (big-endian, also called network byte order, used in network protocols) or least significant first (little-endian, used by x86 and most ARM chips). It matters whenever bytes cross between machines or files are parsed by hand; serialisation formats specify it so you do not have to. Compilers also align fields to their natural boundaries and insert padding, which is why a struct can be larger than the sum of its fields.

Trade-offs

Wider types cost more memory and bandwidth but remove overflow risk; at today's sizes the safety is almost always worth it for IDs and timestamps. Floats trade exactness for range and speed; decimals trade speed for exactness. UTF-8 trades fixed width for compactness and ASCII compatibility, which is why every length limit must name its unit.

In an interview

Volunteer representations when designing schemas and APIs: "IDs are 64-bit, generated time-sortable; money is integer cents with a currency code; timestamps are UTC epoch milliseconds; text is UTF-8 with limits in characters, enforced on code-point boundaries." Mentioning that JavaScript clients lose precision on 64-bit integers, so IDs go over the wire as strings, is a detail that lands well.

Check yourself

An orders table uses a signed 32-bit auto-increment primary key and inserts about 5 million orders a day across all tenants. What happens, and when?

How data is represented · Backplane