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

17. Scaled Fixed-Point in the Wild: A Historical Survey

LESSON 17 | 0001 0001 | 0x11

The concept

Lesson 16 established that a fixed-point value's scale — where the point sits — lives entirely in the programmer's bookkeeping, never in the stored bits themselves. That discipline didn't start with computers. Slide rules and log tables ran on exactly the same principle for centuries before any electronic machine existed: a slide rule gives back significant digits only, worked purely from the physical lengths of two logarithmic scales, with no memory of absolute size at all. Multiply two numbers on one, and it reads out something like "375" — never telling you whether the true answer is 3.75, 37.5, or 0.375. The user supplies that scale every time, from their own rough sense of how big the answer ought to be, entirely outside the instrument. Anyone doing real engineering calculation before electronic computers existed had already spent years practicing exactly the habit Lesson 16 asked of a programmer, just in decimal, by estimation, instead of in binary, by an explicit shift.

Early machines built without floating-point hardware needed that same discipline formalized instead of estimated: every value in a calculation had to be assigned a scale in advance, consistently, and every add, subtract, and multiply had to be tracked against it by hand — exactly Lesson 16's mechanics, chained across a whole program instead of a single operation.

Choosing that scale is a genuine trade-off, not just a bookkeeping inconvenience, and it only becomes visible once you look at a whole calculation rather than one operation in isolation. More integer bits buy headroom against overflow (Lesson 13); more fractional bits buy precision. An 8-bit word has only 8 bits to split between them, and the split has to be chosen once, before the calculation runs, based on knowing — or safely bounding — how large the numbers involved can actually get. Floating point, out of scope here, exists largely to remove this upfront planning entirely, by letting the scale move with each value instead of being fixed for the whole calculation in advance.

Example 1 — the slide rule's ambiguity, worked concretely

Three different multiplications, three wildly different answers, the same three significant digits:

2.5   x 1.5 = 3.75
25    x 1.5 = 37.5
0.25  x 1.5 = 0.375

A slide rule, or a log table, would show the identical reading — "375" — for all three. Nothing in the instrument distinguishes 3.75 from 37.5 from 0.375; only the user, judging roughly how big the real-world answer ought to be, decides which one was meant. This is Lesson 14's "same digits, different value" trap in its original, pre-binary form — the same problem, a full century before there were any bits around to disagree about.

Example 2 — a short calculation, tracked the way a programmer would have

Compute (1.25 + 0.75) x 1.5, scaled by 2^-4 throughout, using only Lesson 16's rules in sequence.

Add first:

1.25 x 16 = 20 = 0001 0100
0.75 x 16 = 12 = 0000 1100

  0001 0100
+ 0000 1100
-----------
  0010 0000

0010 0000 is 32; at scale 2^-4, 32 / 16 = 2.0, matching 1.25 + 0.75 = 2.0.

Now multiply that sum by 1.5 (raw 24 at the same scale):

  0010 0000  (32)
x 0001 1000  (24)
-----------------
  768  =  0000 0011 0000 0000   (16 bits, scale now 2^-8)

Rescale back to 2^-4 with a right shift of 4:

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

The bottom 8 bits, 0011 0000, are 48; at scale 2^-4, 48 / 16 = 3.0 — matching (1.25 + 0.75) x 1.5 = 3.0. Nothing here is new: this is what "running a calculation" meant on a machine like this — a sequence of ordinary adds, multiplies, and corrective shifts, every one of them at a scale chosen and tracked entirely on paper before the program ever ran.

Example 3 — choosing a scale for a whole calculation, not just one step

Suppose a calculation is known in advance to produce intermediate values as large as 20.5, stored in an 8-bit word. Lesson 16's examples all used scale 2^-4 (4 integer bits, 4 fractional bits) without questioning it — but that scale caps the representable integer part at 15. Using it here anyway:

20.5 x 16 = 328   (needs 9 bits — doesn't fit in 8)

Truncated to 8 bits, 328 wraps to 72 (328 - 256), which decodes at scale 2^-4 to 72 / 16 = 4.5 — a completely wrong answer that looks exactly as valid as a correct one, the same silent failure Lesson 13 warned about, now arising from a bad scale choice rather than an unexpected operand.

The fix is to check the range before picking a scale. 20.5 needs enough integer bits to hold up to 20; 2^5 = 32 clears that with room to spare, so 5 integer bits are enough, leaving 3 fractional bits — scale 2^-3. At that split, the largest representable value is 31 + 7/8 = 31.875, safely past 20.5. The cost: one fewer fractional bit than Lesson 16's default, so coarser precision, in exchange for enough headroom that this particular calculation can't silently overflow.

Exercises

Exercise 1. Compute 12 x 12, 1.2 x 12, and 0.12 x 1.2. Confirm all three share the same significant digits, and explain what a slide rule would have shown for each, per Example 1.

Exercise 2. A calculation's intermediate values are known to reach as high as 9.5, stored in an 8-bit word. Using Example 3's method, how many integer bits are needed at minimum, and how many fractional bits remain? Is Lesson 16's default scale (2^-4) safe for this particular calculation?

Exercise 3. A classmate stores a calculation's values at Lesson 16's default scale (2^-4) without checking the range first. One of the calculation's intermediate values turns out to be exactly 20. What raw value results, does it fit in 8 bits, and what does the classmate's machine actually decode instead of 20? What should they have done differently, and at what point in this lesson should they have done it?

Show Answer Key

1. 12 x 12 = 144, 1.2 x 12 = 14.4, and 0.12 x 1.2 = 0.144 — three different true answers sharing the same digit sequence, 144. A slide rule would show that same reading, "144," for all three; nothing about the instrument itself would distinguish a calculation meant to produce roughly 144 from one meant to produce roughly 14 or roughly 0.14 — exactly Example 1's pattern.

2. 9.5's integer part is 9; 2^4 = 16 is the smallest power of two clearing it, so 4 integer bits are enough, leaving 4 fractional bits — scale 2^-4. That's exactly Lesson 16's default scale, and it turns out to be safe for this particular calculation — but only because the range was actually checked, not because 2^-4 is safe by default.

3. 20 x 16 = 320, which needs 9 bits and does not fit in 8. Wrapped into 8 bits, 320 becomes 64 (320 - 256), decoding at scale 2^-4 to 64 / 16 = 4.0 — nowhere near the true value of 20, with nothing in the wrapped result itself flagging that anything went wrong. The classmate should have checked the calculation's expected range and chosen integer/fractional bit counts to fit it, per Example 3, before running any arithmetic at that scale — range-checking is a planning step that happens before the calculation starts, not something the arithmetic itself can catch after the fact.

Part D closes here. Lessons 14-17 have all circled one idea from different angles: a stored bit pattern never carries its own meaning — not whether it's a whole number or a fraction, not where its point sits, not what scale it was chosen at — all of that lives outside the bits, in whatever convention the programmer settled on in advance. Part E makes that the explicit subject: Lesson 18 asks what else, beyond scale, has to be decided the same way before a raw word means anything at all.