← Binary Course — Part D, lesson 3 of 4 (Fractions in binary)

16. Fixed-Point: Tracking Your Own Binary Point By Hand

LESSON 16 | 0001 0000 | 0x10

The concept

Lesson 14 established that the same bits mean different things depending on where you imagine the point — nothing in the bits themselves decides that, only a stated convention does. This lesson asks the next question: what does that mean for arithmetic, on a machine with no separate "fraction mode"?

Look at what Lesson 11's addition rule actually does: line up two 8-bit patterns and add them position by position, carrying as needed. That procedure never once asks what the bits represent. It doesn't know or check whether bit 0 is the ones place or a sixteenth. The same is true of Lesson 12's subtraction, Lesson 5's AND/OR/XOR/NOT, and Lesson 6's shifts — every one of them is purely positional, operating on wherever the bits sit, with no awareness of a point at all. That means if two values are scaled the same way — the point sitting in the same place in both — adding their raw bit patterns with the ordinary whole-number addition rule produces the correct sum at that same scale, automatically. No special circuit, no rewiring, no fraction-aware hardware required. This is fixed-point arithmetic: pick one scale, in advance, for every value that will interact directly, and the plain integer ALU already covered in Parts B and C does the rest.

"Pick a scale" means deciding, once, where the point sits — stated in words: "the point sits after bit 4," or equivalently "scaled by 2^-4." A raw fixed-point word is written exactly like any other 8-bit word from Lessons 2-13 — an ordinary undecorated nibble-grouped pattern, no point shown inside it — with its scale stated alongside, in words, the same way Lessons 14-15 required a stated reading whenever a bare bit pattern could otherwise be ambiguous. The point never appears inside the stored bits on a real machine; it only ever exists in the program's (or programmer's) bookkeeping.

Two operations do need extra care, and it's worth naming both up front. Multiplication changes the scale: multiplying two values that are each scaled by 2^-4 produces a raw product scaled by 2^-8 (the two -4s add), so the raw product must be shifted back afterward to restore the expected scale — an ordinary shift instruction (Lesson 6), not special hardware, but a step that's easy to forget. Addition or subtraction of values at different scales needs the coarser value shifted to match the finer one first — exactly the everyday habit of lining up decimal points before adding 3.500 + 1.25, just done with a bit shift instead of padding zeros by eye.

This lesson keeps every value non-negative, to keep the point-tracking itself uncluttered. Negative fixed-point values need nothing new: Part C's two's complement combines directly with a stated scale, same encoding, same addition rule.

One genuine limit is worth stating plainly: what this lesson does not cover is a per-number, self-describing scale that moves automatically — that's floating point, and it needs real additional hardware (circuitry to compare two numbers' exponents and realign their bits before adding them) that fixed point, with its scale fixed and known in advance, never needs. Floating point stays out of scope here, but it marks the actual dividing line: not "can hardware read a fraction," but "does the scale itself need to be carried and adjusted automatically, per value."

Example 1 — addition at a shared scale

Add 3.5 and 2.25, both scaled by 2^-4 (point after bit 4):

3.5  x 16 = 56  =  0011 1000
2.25 x 16 = 36  =  0010 0100

  0011 1000
+ 0010 0100
-----------
  0101 1100

0101 1100 is 92. At scale 2^-4, that's 92 / 16 = 5.75 — matching 3.5 + 2.25 = 5.75. Notice this used Lesson 11's addition rule with nothing added or changed. The same three bit patterns, read with no scale at all, are just 56 + 36 = 92 — an ordinary whole-number sum. The adder performed one mechanical operation; which of those two readings is "correct" was decided entirely beforehand, by the scale chosen for these particular values, not by anything the adder did differently.

Example 2 — subtraction at a shared scale

Undo Example 1: subtract 36 from 92, still scaled by 2^-4:

  0101 1100
- 0010 0100
-----------
  0011 1000

0011 1000 is 56, and 56 / 16 = 3.5 — matching 5.75 - 2.25 = 3.5, Example 1's other operand recovered exactly. Lesson 12's subtraction rule needed no changes either.

Example 3 — aligning mismatched scales before adding

Add 2.75 (scaled by 2^-4, point after bit 4) and 1.5 (scaled by 2^-1, point after bit 1 — only one fractional bit). These can't be added raw-to-raw; their scales don't match, the same way you wouldn't add 2.750 to 1.5 in decimal without first writing the second as 1.500.

2.75 at scale 2^-4:  2.75 x 16 = 44  =  0010 1100
1.5  at scale 2^-1:  1.5  x 2  =  3  =  0000 0011

1.5's raw value needs to move from scale 2^-1 to scale 2^-4 — three more fractional bits — which is exactly Lesson 6's left shift, shifting by the difference between the two scales:

0000 0011  shifted left 3  =  0001 1000   (still 1.5, now at scale 2^-4)

Now both operands share scale 2^-4 and add normally:

  0010 1100
+ 0001 1000
-----------
  0100 0100

0100 0100 is 68. At scale 2^-4: 68 / 16 = 4.25, matching 2.75 + 1.5 = 4.25.

Example 4 — multiplication and the scale-doubling correction

Multiply 1.5 and 2.5, both scaled by 2^-4:

1.5 x 16 = 24  =  0001 1000
2.5 x 16 = 40  =  0010 1000

Unlike addition, multiplying two 8-bit values can produce a result needing up to twice the width — the same way multiplying two 3-digit decimal numbers can need 6 digits. 24 x 40 = 960, which needs more than 8 bits:

960 = 0000 0011 1100 0000   (16 bits)

That raw product is scaled by 2^-4 x 2^-4 = 2^-8, not the 2^-4 either operand started at — the scales multiplied together when the values did. Restoring scale 2^-4 takes a right shift of 4 bits, undoing the extra factor of 2^-4:

0000 0011 1100 0000  shifted right 4  =  0000 0000 0011 1100

The bottom 8 bits, 0011 1100, are 60. At scale 2^-4: 60 / 16 = 3.75, matching 1.5 x 2.5 = 3.75. The multiply circuit itself did nothing but an ordinary integer multiply; restoring the right scale afterward was a separate, explicit shift.

Exercises

Exercise 1. Add 1.75 and 0.5, both scaled by 2^-4 (point after bit 4), using Example 1's method.

Exercise 2. Add 2.25 (scaled by 2^-4) and 1.5 (scaled by 2^-2 — point after bit 2, two fractional bits), using Example 3's alignment method.

Exercise 3. A classmate insists a computer without a floating-point unit can only add 3.25 and 1.5 correctly if it treats them as whole numbers — that adding them as fractions would need different circuitry. Using Example 1, explain what's wrong with that claim. Then name the one operation covered in this lesson where the classmate's instinct would actually be closer to right — where an ordinary integer circuit alone isn't quite enough without an extra step.

Exercise 4. A classmate multiplies 0.75 and 2.0, both scaled by 2^-4. Their raw values are 0.75 x 16 = 12 and 2.0 x 16 = 32, and 12 x 32 = 384. They then read 384 directly at scale 2^-4 (384 / 16) as the answer, without shifting anything. What's the correct result, and what did they get instead?

Show Answer Key

1. 1.75 x 16 = 28 = 0001 1100. 0.5 x 16 = 8 = 0000 1000. 0001 1100 + 0000 1000 = 0010 0100, which is 36. At scale 2^-4: 36 / 16 = 2.25, matching 1.75 + 0.5 = 2.25.

2. 2.25 at scale 2^-4: 2.25 x 16 = 36 = 0010 0100. 1.5 at scale 2^-2: 1.5 x 4 = 6 = 0000 0110. Aligning to scale 2^-4 needs 2 more fractional bits (shift left 2): 0000 0110 shifted left 2 is 0001 1000 (24, still 1.5 — 24 / 16 = 1.5, unchanged). Adding: 0010 0100 + 0001 1000 = 0011 1100, which is 60. At scale 2^-4: 60 / 16 = 3.75, matching 2.25 + 1.5 = 3.75.

3. The classmate is wrong: Example 1 added the exact same raw bit patterns and got the correct answer whether they're read as 56 + 36 = 92 (whole numbers) or 3.5 + 2.25 = 5.75 (fixed-point at scale 2^-4) — the adder did one mechanical, positional operation either way, and which reading applies was decided beforehand by the chosen scale, not by anything the circuit did differently. The operation where extra care really is needed is multiplication (Example 4): the raw product lands at a doubled scale and needs an explicit corrective shift before it means what it's supposed to mean.

4. The correct result: 12 x 32 = 384, scaled by 2^-8 (the two 2^-4 scales multiplied together), so it needs a right shift of 4 before reading it at scale 2^-4: 384 shifted right 4 is 24, and 24 / 16 = 1.5, matching 0.75 x 2.0 = 1.5. The classmate skipped that shift and read the unshifted raw product directly at scale 2^-4 instead: 384 / 16 = 24.0 — off by a factor of 16, not a small rounding slip, because they applied the wrong scale to a value that was never at that scale to begin with.

Lesson 17 turns from mechanics to history: how scaled fixed-point arithmetic — exactly what this lesson just did by hand — was standard practice on real early machines (and on the slide rules that came before them) long before floating-point hardware existed.