Binary, and Why Everything Is Eventually Bits
Binary is not a numbering curiosity you convert for exam questions. It is the substrate: instructions, addresses, permission flags, protocol headers and floating-point values are all bit patterns, and hexadecimal exists because humans cannot read them otherwise.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
The same bits, several different meanings
The reason binary matters is not conversion arithmetic. It is that hardware stores patterns and attaches no type to them. A 32-bit word holding 0x41424344 is simultaneously the integer 1094861636, the four ASCII characters ABCD, a plausible memory address, and — on some encodings — a valid machine instruction. Which one it *is* depends entirely on what reads it.
This is why casting between a float and an integer of the same width can either preserve the value or reinterpret the pattern, and why those are two completely different operations that look similar in source. It is why a buffer overflow can turn data into an instruction pointer. And it is why every protocol specification defines fields by bit offset rather than by number: the number is downstream of the layout.
The practical habit this should build is asking, when something surprising happens to a value, *what pattern is actually stored* — not what the variable is declared as. A debugger showing hex is showing you the ground truth; the decimal rendering is an interpretation the tool chose for you.
| Interpretation | Value | Where you would meet it |
|---|---|---|
| Unsigned integer | 1094861636 | A counter, a length, an index |
| Signed integer (two's complement) | 1094861636 | Positive here; the top bit is clear — see Two's Complement: One Circuit for Addition and Subtraction |
| Four ASCII bytes | ABCD | A file magic number, a protocol tag |
| IEEE-754 single-precision float | approximately 12.14 | Reinterpreted, not converted — see Floating Point: Trading Precision for Range |
| Memory address | 0x41424344 | A pointer; famously what you see after corrupting one with text |
Hexadecimal, and why it is everywhere
Hex is not a third number system anybody thinks in. It is a compression of binary for human eyes: exactly four bits per hex digit, so the mapping is positional and lossless, and you can read a byte as two digits without doing arithmetic. Decimal has no such alignment — 200 tells you nothing about which bits are set, whereas 0xC8 tells you immediately that it is 1100 1000.
That alignment is the entire reason tooling speaks hex. Memory dumps, addresses, colour values, permission bits, hash digests and instruction encodings are all displayed in hex because the display preserves the bit structure. Once you can convert a hex digit to four bits by reflex, a memory dump stops being noise.
The dump below is the sort of thing a debugger shows. Note that the address column, the byte column and the interpretation column are three views of the same storage — and that the ASCII column is only meaningful because someone decided those bytes were text.
Address Bytes (hex) ASCII
0x7ffd4a20 48 65 6c 6c 6f 20 77 6f 72 6c 64 00 00 00 00 00 Hello world.....
0x7ffd4a30 2a 00 00 00 ff ff ff ff 00 00 80 3f 00 00 00 00 *..........?....
^^ 42 ^^ -1 as int32 ^^ 1.0f as float
Each hex digit is exactly four bits:
0x2a = 0010 1010 = 42
0xff = 1111 1111 = 255 unsigned, -1 as signed 8-bit
0x3f800000 = 1.0 interpreted as IEEE-754 single precisionBit operations are how hardware asks questions
Because values are patterns, the cheapest possible operations are the ones that work on patterns directly: AND, OR, XOR, NOT and shifts. Each is a single gate per bit and completes in one cycle on essentially every CPU, which makes them the fastest arithmetic available.
The idiom that follows is the mask: build a pattern with ones where you care, AND it against the value, and you have isolated a field. Every permission system, flag register, protocol header parser and packed struct uses this, because packing several small values into one word and extracting them with masks costs almost nothing.
What makes masks worth understanding rather than copying is that they explain why certain "clever" optimisations are not clever at all. x & 7 is genuinely equivalent to x % 8 for unsigned x because 8 is a power of two and the low three bits *are* the remainder — but the compiler already knows that, and writing it by hand buys nothing while costing readability. The value is in reading such code, not producing it.
1// A packed RGBA colour: four 8-bit fields in one 32-bit word.2const r = 0x12, g = 0x34, b = 0x56, a = 0x783 4const packed = (r << 24) | (g << 16) | (b << 8) | a // 0x123456785 6// Extract by shifting the field down, then masking off everything above it.7const red = (packed >>> 24) & 0xff // 0x128const green = (packed >>> 16) & 0xff // 0x349const blue = (packed >>> 8) & 0xff // 0x5610const alpha = packed & 0xff // 0x7811 12// Testing a flag is an AND; setting is an OR; toggling is an XOR.13const READ = 0b100, WRITE = 0b010, EXEC = 0b00114let perms = READ | WRITE // 0b11015const canWrite = (perms & WRITE) !== 0 // true16perms = perms & ~WRITE // clear it: 0b10017perms = perms ^ EXEC // toggle it: 0b101Key points
- Hardware stores patterns and attaches no type; the type is an interpretation applied by whatever reads the bits.
- The same 32 bits can be an integer, a float, four characters, an address or an instruction.
- Hex is used everywhere because four bits map to one digit exactly, preserving the bit structure that decimal destroys.
- Bitwise operations are one gate per bit and among the cheapest instructions available.
- Masks let several small fields share one word and be extracted almost for free — the basis of flags, permissions and packed formats.
Binary Explorer
Change an input and watch which number moves — and which one refuses to.
Each bit is worth twice the one to its right. Nothing about the pattern says whether it is a number, a character, part of an instruction or an address — that is decided entirely by what reads it.
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Value → storage: a value is written as a pattern of bits into a register or a memory location, with no record of its type.
- 2Instruction → interpretation: the instruction that reads it decides what the pattern means — an integer add and a float add read the same bits differently.
- 3Mask → AND gate array: a bitwise AND drives one gate per bit position in parallel, completing in a single cycle.
- 4Shift → barrel shifter: a shift moves every bit by a fixed distance in dedicated hardware rather than by repeated addition.
- 5Result → register: the output is another pattern, again with no type attached, ready for the next interpretation.
- • Believing a bit trick is faster than the equivalent arithmetic because it looks lower-level — the compiler emits the same instructions for both.
- • Confusing a value conversion with a reinterpretation, which produces wildly wrong numbers rather than a compile error in languages that allow it.
- • Treating hex as a different kind of number rather than as a rendering of the same bits.
- • Assuming a bit pattern that renders as sensible text actually is text; it is a coincidence of encoding, and a common source of false confidence when debugging.
Consequences, controls and cost
- • Reinterpreting a pattern (a "bit cast") and converting a value are different operations that can look nearly identical in source.
- • Packed representations save memory and bandwidth at essentially no computational cost, which is why protocols and file formats use them heavily.
- • A corrupted value often displays as recognisable text or a nonsensical pointer, which is a useful diagnostic signal rather than noise.
- • Reading hex fluently turns memory dumps, disassembly and protocol traces from opaque output into readable evidence.
- • Read hex directly rather than converting to decimal; four bits per digit makes the bit pattern visible at a glance.
- • Use masks and shifts to pack related small fields when memory or bandwidth matters — the extraction cost is negligible.
- • Prefer named constants over literal masks; the operation is cheap but an undocumented magic number is not.
- • Do not hand-optimise arithmetic into bit tricks for speed; compilers already do this, and the readability cost is real.
- • Print values in hex when debugging anything representational — an unexpected `0xffffffff` or `0x41414141` identifies the bug immediately.
- • Inspect the compiler output to confirm that arithmetic you expected to become a shift actually did, rather than assuming it.
- • Use a debugger's memory view to see the stored pattern rather than the language's rendering of it.
- • Packed fields save space but make values harder to read, harder to change later, and easier to get wrong when the layout evolves.
- • Bit manipulation is dense; code that is clever with masks is disproportionately hard to review and to modify safely.
- • Fixed-width packing bakes in limits — a field that was generously sized eventually is not.
Scope
§224 — what these claims are specific to.
- GENERALBinary storage and bitwise operations are universal across essentially all contemporary hardware. Shift semantics for negative or oversized shift counts are language-defined and differ — in C++ some are undefined behaviour, in JavaScript the count is masked to five bits.
- ABI-SPECIFICHow multi-byte values are laid out in memory is a platform decision, not a property of binary — see Endianness: Which Byte Comes First.