← Binary Course — Part E, lesson 1 of 4 (Reading real machine words)

18. A Word Is Just Bits, Until You Decide What It Means

LESSON 18 | 0001 0010 | 0x12

The concept

Every operation across Parts B, C, and D — AND/OR/XOR/NOT, shifts, two's-complement addition and subtraction, fixed-point arithmetic — has worked the same way: purely positionally, on wherever the bits sit, never once asking what those bits are supposed to represent. Along the way, three separate lessons each noticed the same gap from a different angle. Lesson 9 pointed out that a word's bits never announce whether they should be read as sign-magnitude, or as a plain unsigned number, or under any other convention — "that choice of convention lives outside the bits, in whichever operation reads the word." Lesson 14 showed the identical six bits reading as 24 or as 0.75 depending only on where a point is imagined. Lesson 16 stated it for scale specifically: "the point never appears inside the stored bits on a real machine; it only ever exists in the program's (or programmer's) bookkeeping." This lesson makes that recurring observation the explicit, central subject: a stored word is not self-explanatory. Its meaning always comes from somewhere outside the bits themselves.

That "somewhere outside" is never mysterious — it's one of a few concrete things:

  • Which instruction reads the word. A signed-compare instruction and an unsigned-compare instruction can read the identical bits and reach opposite conclusions about which of two numbers is larger.
  • A convention stated alongside the program, the way Lesson 16 insisted a fixed-point value's scale always be written in words next to the word itself, because nothing in the bits carries it.
  • The layout a programmer or compiler chose, the way Lesson 8's packed word only means "three small fields" because something decided in advance to slice it that way — the same eight bits are just as validly one number.

By now this course has built the machinery for several distinct readings of the same raw bits: a plain unsigned integer (Parts A-B), a signed integer under sign-magnitude, one's complement, or two's complement (Part C), a fixed-point value at some stated scale (Part D), and a packed record of independent fields (Lesson 8). Real machines layer on more — a character code, a memory address, an instruction's own opcode and operand — but every one of them is the same story: a convention, chosen externally, applied to bits that would otherwise sit there meaning nothing in particular.

It's worth naming one refinement here, because it's tempting to think some formats escape this entirely. Some encodings do carry part of their own description inside the word — floating point is the standard example, where a portion of the bits is reserved to record that value's own scale, so it travels with the number instead of living in separate program bookkeeping the way Part D's fixed point does. That's a real difference, but it only narrows the gap; it doesn't close it. Reading those scale bits correctly still depends on already knowing the word is a float in the first place, rather than a plain integer using those same bit positions for something else entirely — and that first-order decision is made exactly the same way as everywhere else in this lesson: by which instruction the program issues to read the word, never by anything the word itself announces.

This has a sharp practical consequence. Nothing at the hardware or assembly level stops a program from reading a word under the wrong convention. There's no error, no crash — the operation runs, produces a bit pattern, and that pattern gets decoded as whatever convention the instruction assumed, correct or not. A confidently wrong number is the only symptom. Higher-level languages exist partly to close this gap: a type system tracks which convention applies to which value and only ever emits instructions that match, refusing to compile code that would read a value under the wrong one (short of an explicit, deliberate unsafe cast). That's a guarantee the language adds on top; the hardware itself, underneath, enforces nothing at all.

Example 1 — One word, four different signed readings

Take the 8-bit word 1001 0011 — Lesson 9's own worked value, 0x93, carried through Lessons 9-11 as the running comparison of how the three signed schemes encode -19. Read here with no assumed convention at all, decoded four different ways:

plain unsigned:         1001 0011  =  147
sign-magnitude:          sign 1, magnitude 0010011 (19)   ->  -19
one's complement:        invert -> 0110 1100 (108)        ->  -108
two's complement:        invert, +1 -> 0110 1101 (109)    ->  -109

Four different numeric values, from the exact same eight bits, with no step of the decoding process touching the bits themselves differently — only the rule applied after reading them changes. This is the payoff Lesson 9's answer key pointed toward without naming yet: "the clearest possible demonstration that a bit pattern's meaning is never self-announcing."

Example 2 — Scale is a second, independent choice on top of sign

The same word, now also read at a stated fixed-point scale of 2^-4 (point after bit 4, Lesson 16's convention), stacked on top of two of Example 1's sign readings:

unsigned reading (147) at scale 2^-4:        147 / 16  =  9.1875
two's-complement reading (-109) at scale 2^-4:  -109 / 16  =  -6.8125

Sign convention and scale are separate decisions, made independently — nothing forces one to imply the other. Four sign readings from Example 1, each of which could in principle be paired with its own stated scale, means one 8-bit pattern already supports far more than four distinct values depending on what's decided about it. None of that branching exists inside the bits; it's entirely a property of the conventions brought to them.

Example 3 — A convention that doesn't always apply

Conventions don't just disagree on value — sometimes one simply has nothing to say about a given pattern. Read 0110 0001 under the 7-bit ASCII character-code convention:

0110 0001  =  97 (unsigned)  ->  the character 'a'

That works cleanly: 97 is a defined ASCII code point. Now apply the same convention to Example 1's word, 1001 0011 (147 unsigned): standard 7-bit ASCII only assigns meanings to the range 0-127, and 147 falls outside it entirely. There is no letter this represents under that convention — not "the wrong letter," but no defined answer at all. And exactly as the concept section warned, nothing stops a program from trying anyway: it will hand back whatever bit pattern happens to live at that position in some extended table, or garbage, with no error raised to flag that the convention never covered this input to begin with.

Example 4 — Even the format itself is an external decision

Suppose a stored 32-bit word holds a floating-point value — reserving, say, some of its bits for an exponent that records that number's own scale, the self-describing detail the concept section named. A program has two different instructions available that can both read those exact same 32 bits: an integer-add instruction, which treats every bit as an ordinary two's-complement magnitude bit the way Part C's rules do, and a float-add instruction, which treats some of those same bits as the self-carried exponent instead. Both instructions will run. Both will produce a result. Only one of them corresponds to what the value actually stores — and which one is correct depends entirely on which instruction the compiler chose to emit, based on the source language's declared type for that value, never on anything readable from the 32 bits themselves. The exponent field solves how to recover a float's scale once you know it's a float; it does nothing to solve whether a given word is a float at all.

Exercises

Exercise 1. Decode the word 1010 1100 (Lesson 6's running value) three ways, using Example 1's method: as a plain unsigned integer, as sign-magnitude, and as two's complement.

Exercise 2. Using Exercise 1's word and Example 2's method, give its value at a stated fixed-point scale of 2^-4, once under the unsigned reading and once under the two's-complement reading.

Exercise 3. A program reads a stored 32-bit value using an integer instruction when the value was actually written as a float. Using Example 4, explain why this doesn't produce a crash or an error at the hardware level, what happens instead, and name the mechanism most high-level languages rely on to stop this particular mistake from compiling in the first place.

Exercise 4. A classmate claims that since two's complement is how negative numbers are usually represented, any 8-bit word whose leftmost bit is 1 must represent a negative number. Using Example 1's word, 1001 0011, explain what's wrong with that claim.

Show Answer Key

1. Unsigned: 1010 1100 = 172. Sign-magnitude: sign 1, magnitude 0101100 = 44, so -44. Two's complement: invert (0101 0011), add 1 (0101 0100 = 84), so -84.

2. Unsigned reading (172) at scale 2^-4: 172 / 16 = 10.75. Two's-complement reading (-84) at scale 2^-4: -84 / 16 = -5.25.

3. Nothing about the stored bits stops the integer instruction from running — it has no way to know, and no way to check, that those particular 32 bits were meant to be read as a float. It simply applies plain two's-complement rules to every bit and produces some resulting value, confidently and without complaint; the answer is just wrong, not flagged as wrong, because the mismatch happened one level below where any error-detection could occur. The mechanism that normally prevents this is a compile-time type system: the compiler tracks that a given value is a float and never emits anything but float instructions for it, making the mismatched-instruction mistake impossible to write by accident (short of a deliberate, explicit unsafe cast) — a guarantee built on top of hardware that, on its own, enforces no such thing.

4. The claim is false, and 1001 0011 is a direct counterexample: read under the plain unsigned convention (Example 1), that same word is 147 — a perfectly ordinary positive number, no sign or negativity involved at all. Bit 7 being 1 only means "negative" under a signed convention that has specifically designated it the sign bit; as Lesson 9 put it, that bit "is not stored anywhere separate from the rest of the word... no different in kind from bit 6 or bit 3." Whether it's being read that way at all is exactly the external decision this lesson has been about.

Lesson 19 puts all of this into practice on an actual historic machine word — no longer an abstract 8-bit pattern, but a real word from a real machine, decoded field by field using every reading this lesson named.