⏱️ Reading time: 14 min

The 8087 microcode multiplied floating-point addition speed by nearly 100 times compared to the 8086 alone, using just 1,648 microinstructions etched into its ROM. That leap turned the IBM PC into a machine capable of recalculating spreadsheets and engineering blueprints without waiting minutes for each operation.

📑 En este artículo
  1. TL;DR
  2. What is the 8087 microcode?
  3. Why hardware arithmetic matters
  4. The 80-bit format: how the 8087 stores numbers
  5. How the 8087 aligns and adds two floating-point numbers
  6. Code: simulating alignment and addition like the 8087
  7. How subtraction works: cancellation and left normalization
  8. How to try it
  9. Inside the datapath: adder, shifter, and exponent converter
  10. Special cases: infinities, zeros, and signs
  11. Comparison: from software to the 8087 to modern x87
  12. Real-world use cases
  13. Common mistakes and gotchas
  14. Going deeper: why the shifter takes up so much die space
  15. Frequently Asked Questions
    1. What makes the 8087 microcode different from software?
    2. Why does the 8087 math coprocessor need such a large shifter?
    3. What happens if a subtraction on the 8087 results in zero?
    4. Does the 8087’s FPU handle infinities and NaN values?
    5. Is the instruction set inherited from the 8087 still used in current CPUs?
  16. References

A recent analysis of the 8087 die, done with a real decapped chip photographed under a microscope, reconstructed step by step how its circuitry executes a floating-point addition or subtraction, including all the edge cases involving infinities and signed zeros.

TL;DR

  • The 8087 math coprocessor executes 80-bit floating-point addition and subtraction with 1,648 microinstructions.
  • Aligning the significands before adding is the same trick as aligning decimal points, but in binary.
  • The 8087’s shifter takes up a huge portion of the die because it shifts an arbitrary number of bits in a single pass.
  • Subtracting two close numbers can require normalizing with a shift of several bits to the left.
  • Python with math.frexp and math.ldexp reproduces the same alignment and normalization algorithm as the 8087.

What is the 8087 microcode?

The 8087 microcode is the sequence of 1,648 microinstructions etched into the central ROM of Intel’s first math coprocessor, launched in 1980, which controls step by step how the adder, shifter, and exponent converter execute an 80-bit floating-point addition or subtraction.

Intel sold it as a separate chip for the 8086 and 8088, the processor IBM chose for its original PC. Without a coprocessor, those CPUs resolved decimals through pure integer instructions, with software routines that took hundreds of cycles per operation.

With the 8087 installed, the same calculation ran on dedicated circuitry: an integer adder, a shifter capable of moving bits in a single pass, and an exponent converter that detects overflows before they ruin the result.

Why hardware arithmetic matters

Before the 8087, a program adding two decimals on the 8086 had to break the operation down into integer instructions: extract the exponent, align bits by hand, and reassemble the result. Each step cost clock cycles, and a single floating-point addition could take as long as dozens of integer additions.

The 8087 solved that bottleneck by putting the alignment and normalization logic directly into silicon. According to the die analysis published on Ken Shirriff’s blog, the speed improvement for addition reached nearly 100 times compared to the 8086 alone, and more complex operations, like square roots or logarithms, gained even more.

By 1980, that meant a spreadsheet with thousands of numeric cells, or a computer-aided design program, could recalculate in seconds instead of minutes. That was the 8087’s selling point for engineering workstations and early software versions like Lotus 1-2-3.

The 8087 hit the market in 1980 as an optional coprocessor for the 8086 and 8088. Foto de Michael Dziedzic en Unsplash

The 80-bit format: how the 8087 stores numbers

Internally, the 8087 doesn’t work with the 32-bit float or the 64-bit double that modern languages use. All its operations happen in a proprietary 80-bit format Intel called temporary real, with more significand bits than any other format the chip exposed to the programmer.

That extended format absorbs the rounding error of intermediate calculations before the final result is converted to 32 or 64 bits. That’s why a long sequence of additions and subtractions on the 8087 loses less precision than the same sequence done directly in short format, since each intermediate step keeps extra bits that only get discarded at the end of the chain.

FormatTotal bitsSignificand bitsApproximate range
Short real3224exceeds 10⁻³⁶ to 10³⁸
Long real6453much larger than short real
Temporary real8064the widest of the three, internal to the chip only

How the 8087 aligns and adds two floating-point numbers

A floating-point number is stored as sign times significand times 2 raised to the exponent. The significand follows the form 1.bbb…, a leading 1 followed by the binary point and the rest of the bits. So the binary 1.1 represents 1 + 1/2, meaning 1.5 in decimal, and 1.0011 represents 1 + 1/8 + 1/16, or 1.1875.

To add two numbers, their exponents must first be equalized before adding the significands, just like aligning the decimal points of 13.5 and 3.5 before adding them by hand. The 8087 expresses 13.5 as 1.6875 times 2 to the third power and 3.5 as 1.75 times 2 to the first. The exponent difference is 2, so the chip shifts the significand of 3.5 two positions to the right before adding.

flowchart TD
    A["Take the two operands"] --> B["Compare exponents"]
    B --> C["Shift the smaller significand"]
    C --> D["Add the aligned significands"]
    D --> E{"Was there extra carry?"}
    E -->|"yes"| F["Shift 1 bit right and add 1 to the exponent"]
    E -->|"no"| G["Result already normalized"]
    F --> G

Adding the already-aligned significands gives, in this case, a result that overflows one bit to the left. The 8087 normalizes by shifting that result one position to the right and adding 1 to the exponent, until the most significant bit is a 1 again. The final result is 17.0, the same as adding 13.5 plus 3.5 by hand.

Code: simulating alignment and addition like the 8087

The same algorithm of aligning, adding, and normalizing can be reproduced in a few lines of Python using math.frexp, which separates a number into significand and exponent, and math.ldexp, which recombines them.

import math

def align_and_add(a, b):
    ma, ea = math.frexp(a)
    mb, eb = math.frexp(b)
    if ea >= eb:
        mb = mb / (2 ** (ea - eb))
        exp = ea
    else:
        ma = ma / (2 ** (eb - ea))
        exp = eb
    mantissa = ma + mb
    if abs(mantissa) >= 1.0:
        mantissa /= 2
        exp += 1
    return math.ldexp(mantissa, exp)

print(align_and_add(13.5, 3.5))

The output is 17.0, the same result the 8087 calculated in the alignment example above.

How subtraction works: cancellation and left normalization

Subtraction is trickier than addition. The result can be positive, negative, or exactly zero, and when two numbers are close, the result ends up much smaller than both operands. The 8087 aligns the exponents the same way as in addition, but after subtracting the significands, the most significant bit almost never lands in the right position, so normalization usually requires shifting several bits to the left.

flowchart TD
    A["Align exponents"] --> B["Subtract the significands"]
    B --> C{"Is the result zero?"}
    C -->|"yes"| D["Return +0 or -0 based on sign"]
    C -->|"no"| E{"Is the significand negative?"}
    E -->|"yes"| F["Make it positive and flip the result's sign"]
    E -->|"no"| G["Normalize by shifting left"]
    F --> G

Let’s take 8 minus 7.5. The chip expresses 8 as 1.0 times 2 to the third power and 7.5 as 1.875 times 2 squared; once aligned, it subtracts 1.0 minus 0.9375 and gets 0.0625. To normalize that result until the lead bit is 1, the 8087 shifts four positions to the left and subtracts 4 from the exponent, arriving at 1.0 times 2 to the negative first power, or 0.5.

If the significand of the subtraction ends up negative, the 8087 converts it to positive and flips the result’s sign. And if the subtraction gives exactly zero, there’s nothing to normalize: the chip returns directly +0 or -0 depending on the corresponding sign.

import math

def align_and_sub(a, b):
    ma, ea = math.frexp(a)
    mb, eb = math.frexp(b)
    if ea >= eb:
        mb = mb / (2 ** (ea - eb))
        exp = ea
    else:
        ma = ma / (2 ** (eb - ea))
        exp = eb
    mantissa = ma - mb
    shifts = 0
    while mantissa != 0 and abs(mantissa) < 0.5:
        mantissa *= 2
        exp -= 1
        shifts += 1
    return math.ldexp(mantissa, exp), shifts

print(align_and_sub(8.0, 7.5))

The output is (0.5, 4): the correct result and the four shift bits predicted by the die analysis, calculated with the same alignment, subtraction, and normalization logic.

How to try it

Both examples run on plain Python 3 without installing anything extra: the math library is part of the standard library. Save either block to a file called align_8087.py and run it with python3 align_8087.py on Windows, macOS, or Linux; all three systems ship with or allow installing Python 3 from the same official distribution.

Inside the datapath: adder, shifter, and exponent converter

flowchart TD
    A["Register B"] --> C["Integer adder"]
    D["Temporary registers or stack"] --> C
    C --> E["Sum register"]
    E --> F["Shifter"]
    F --> G["Normalization"]
    H["Leading zero counter"] --> F
    I["Exponent converter"] --> G

The 8087’s datapath takes up the lower half of the die and groups together the blocks that do the heavy lifting. The integer adder, at the center, receives one operand from register B and another from the temporary registers or the chip’s eight-register stack; its output goes to the sum register.

The shifter is, according to the die analysis, the block that takes up the most space within that half of the chip, because its circuitry lets it move a value an arbitrary number of positions in a single pass, instead of shifting bit by bit. The significand alignment operation described above depends entirely on this component.

The 8087’s shifter can shift a value an arbitrary number of bits in a single pass. Foto de Chris Ried en Unsplash

Working alongside the shifter is a leading zero counter, which counts how many bits or bytes are left over on the left side of a value to know how much it needs to shift during normalization. The exponent converter, meanwhile, processes the exponents of both operands, detects when a result overflows or runs out of range, and adjusts the final exponent.

💭 Key point: The 8087 doesn’t gain speed just by having more transistors: it gains speed because it replaces a software loop, shifting bit by bit, with a circuit that resolves the full shift in a single cycle.

Special cases: infinities, zeros, and signs

Beyond normal arithmetic, the 8087 microcode dedicates a good chunk of its 1,648 microinstructions to resolving edge cases. Adding infinity plus a finite number gives infinity; adding positive infinity and negative infinity has no valid numeric result, so the chip returns a special value called NaN, for not a number.

Subtracting two infinities with the same sign is also indeterminate, and the result is also NaN. These cases aren’t resolved by the adder: the chip detects them beforehand, by comparing the exponent and significand bits that identify an infinity or a NaN, and jumps straight to the correct result without going through the alignment logic.

The case of signed zero is subtler. If a subtraction gives exactly zero, the 8087 can’t normalize an empty value, so it returns +0 or -0 depending on the sign the calculation would have given. That distinction matters in later operations, like dividing by the result: 1 divided by +0 gives positive infinity, but 1 divided by -0 gives negative infinity.

Comparison: from software to the 8087 to modern x87

The way floating-point addition gets resolved has changed several times since 1980. This table summarizes the main options and when each one made sense.

OptionWhen it was usedAdvantageLimitation
Software on the 8086Systems without a coprocessor, integer-only codeNeeds no additional hardwareEach floating-point addition costs dozens or hundreds of cycles
8087 coprocessorIBM PC and clones with a socket for the separate chipAlignment and normalization in dedicated circuitryExtra chip, its own socket, and higher power draw
x87 integrated since the 486DXx86 CPUs from the 90s and 2000s with 80-bit registersSame instruction set, no separate chipRigid register stack, hard for the compiler to optimize
SSE2 and AVXModern x86-64 code with single or double precisionFlat registers, vectorizes several operations at onceDoesn’t offer the original x87’s extended 80-bit precision

Real-world use cases

Lotus 1-2-3 and the earliest spreadsheets for the IBM PC recommended the 8087 to recalculate thousands of decimal cells without locking up the machine. Computer-aided design programs of the era, which multiplied matrices to project 3D surfaces, relied on the coprocessor even more: every perspective view involved thousands of floating-point additions and multiplications.

Engineering workstations and early numerical simulation packages also sold the 8087 as an almost mandatory upgrade. By 1985, the successor 80287 came factory-integrated on several 80286-based boards, a sign that the market already took having an onboard math coprocessor for granted.

Common mistakes and gotchas

  • Assuming subtraction is symmetric to addition: subtraction has to handle negative signs and the zero-result case, something addition never faces.
  • Ignoring catastrophic cancellation: subtracting two very close numbers leaves few significant bits in the result, even though the operation looks trivial.
  • Thinking x87 is still the fast path: modern x86-64 compilers prefer SSE2 or AVX, and reserve x87 almost exclusively for C’s long double type.
  • Forgetting infinity and NaN cases when validating results: an addition with an infinite operand doesn’t trigger an exception, it returns another infinity or a NaN depending on the case.
  • Assuming the shift is free: the 8087’s shifter resolves it in a single pass, but software that emulates it bit by bit pays a cost that grows with the shift size.

Going deeper: why the shifter takes up so much die space

A bit-by-bit shifter, which moves one position per cycle, is cheap in transistors but slow when it has to shift the full 64 bits of the significand. The 8087 solves this with a barrel shifter: several layers of multiplexers where each layer decides whether to shift the value by a fixed amount of positions (1, 2, 4, 8…), and the combination of layers allows any shift amount in a single pass.

That design costs silicon area, because each layer of multiplexers needs as many cells as the data has bits, multiplied by the number of layers. That cost explains why, within the datapath, the shifter block ends up larger than the integer adder block, even though the adder performs arithmetic that looks more complex.

The leading zero counter follows similar logic: instead of checking bit by bit from the left until it finds the first 1, it uses a tree of comparators that reduces the search to just a few logic levels. Without that circuit, every normalization of a subtraction with numeric cancellation would take time proportional to the shift size, something the chip couldn’t afford if it wanted to keep its speed advantage over the 8086.

Your next step: run align_and_sub with another pair of close numbers, like 100.25 and 99.75, and confirm by hand how many shift bits the normalization predicts before running the code.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

What makes the 8087 microcode different from software?

The 8087 microcode runs inside the chip and controls dedicated circuitry (adder, shifter, exponent converter) in parallel. A software routine on the 8086 executes the same alignments and normalizations with generic integer instructions, step by step.

Why does the 8087 math coprocessor need such a large shifter?

Because aligning exponents and normalizing results requires shifting significands of up to 64 bits. Doing it in a single cycle, instead of bit by bit, is what gives the chip its speed advantage over emulating the operation in software.

What happens if a subtraction on the 8087 results in zero?

The chip has nothing to normalize, so it directly returns the special value +0 or -0, depending on the sign the result would have had before simplifying it.

Does the 8087’s FPU handle infinities and NaN values?

Yes: the 8087 detects these cases by comparing the exponent and significand bits before entering the addition or subtraction logic, and returns infinity or NaN directly without going through the adder or the shifter.

Is the instruction set inherited from the 8087 still used in current CPUs?

The x87 set is still present in x86-64 processors for compatibility, but modern compilers prefer SSE2 or AVX for floating-point and reserve x87 almost exclusively for C’s long double type.

References

  • Righto.com: die analysis of the Intel 8087 and its floating-point addition and subtraction microcode.
  • Wikipedia: history and specifications of the Intel 8087 math coprocessor.
  • Wikipedia: IEEE 754 standard for floating-point number representation.
  • Wikipedia: extended 80-bit format used by x87, inherited from the 8087.

📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion

Featured image: Foto de Daniel Pantu en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: Tech NewsTutorials

Javier Alarcón

Infrastructure engineer specializing in networking, Linux systems, Kubernetes, and cloud architectures. Covers hardware, networking, observability, and engineering practices for production teams.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.