← Binary Course — Part D, lesson 4 of 4 (Fractions in binary)
17. Scaled Fixed-Point in the Wild: A Historical Survey
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.
Sign in to mark lessons complete and keep your progress.