⏱️ Lectura: 16 min

El microcódigo del 8087 multiplicó casi 100 veces la velocidad de la suma en punto flotante frente al 8086, usando apenas 1.648 microinstrucciones grabadas en su ROM. Ese salto convirtió a la PC de IBM en una máquina capaz de recalcular hojas de cálculo y planos de ingeniería sin esperar minutos por cada operación.

📑 En este artículo
  1. TL;DR
  2. ¿Qué es el microcódigo del 8087?
  3. Por qué importa la aritmética en hardware
  4. El formato de 80 bits: así guarda los números el 8087
  5. Cómo el 8087 alinea y suma dos números en punto flotante
  6. Código: simulando la alineación y la suma como el 8087
  7. Cómo funciona la resta: cancelación y normalización a la izquierda
  8. Cómo probarlo
  9. Dentro del datapath: sumador, shifter y conversor de exponente
  10. Casos especiales: infinitos, ceros y signos
  11. Comparativa: del software al 8087 y al x87 moderno
  12. Casos de uso reales
  13. Errores comunes y gotchas
  14. Profundizando: por qué el shifter ocupa tanto espacio en el die
  15. Preguntas frecuentes
    1. ¿Qué hace distinto al microcódigo del 8087 frente al software?
    2. ¿Por qué el coprocesador matemático 8087 necesita un shifter tan grande?
    3. ¿Qué pasa si una resta en el 8087 da como resultado cero?
    4. ¿El FPU del 8087 maneja infinitos y valores NaN?
    5. ¿Sigue usándose el conjunto de instrucciones heredado del 8087 en CPUs actuales?
  16. Referencias
    1. 📚 Artículos relacionados

Un análisis reciente del die del 8087, hecho con un chip real decapado y fotografiado al microscopio, reconstruyó paso a paso cómo su circuitería ejecuta una suma o una resta en punto flotante, con todos los casos límite que involucran infinitos y ceros con signo.

TL;DR

  • El coprocesador matemático 8087 ejecuta suma y resta en punto flotante de 80 bits con 1.648 microinstrucciones.
  • Alinear los significandos antes de sumar es el mismo truco que alinear comas decimales, pero en binario.
  • El shifter del 8087 ocupa una porción enorme del die porque desplaza una cantidad arbitraria de bits en un solo paso.
  • Restar dos números cercanos puede exigir normalizar con un desplazamiento de varios bits a la izquierda.
  • Python con math.frexp y math.ldexp reproduce el mismo algoritmo de alineación y normalización del 8087.

¿Qué es el microcódigo del 8087?

El microcódigo del 8087 es la secuencia de 1.648 microinstrucciones grabadas en la ROM central del primer coprocesador matemático de Intel, lanzado en 1980, que controla paso a paso cómo el sumador, el shifter y el conversor de exponente ejecutan una suma o resta en punto flotante de 80 bits.

Intel lo vendió como chip aparte para el 8086 y el 8088, el procesador que IBM eligió para su PC original. Sin coprocesador, esas CPU resolvían los decimales a pura instrucción entera, con rutinas de software que tardaban cientos de ciclos por cada operación.

Con el 8087 instalado, el mismo cálculo se ejecutaba en circuitería dedicada: un sumador entero, un shifter capaz de mover bits en una sola pasada y un conversor de exponente que detecta desbordes antes de que arruinen el resultado.

Por qué importa la aritmética en hardware

Antes del 8087, un programa que sumara dos decimales en el 8086 debía descomponer la operación en instrucciones enteras: extraer el exponente, alinear bits a mano y recomponer el resultado. Cada paso costaba ciclos de reloj, y una sola suma en punto flotante podía tardar lo mismo que decenas de sumas enteras.

El 8087 resolvió ese cuello de botella metiendo la lógica de alineación y normalización directamente en silicio. Según el análisis del die publicado en el blog de Ken Shirriff, la mejora de velocidad en la suma llegó a casi 100 veces frente al 8086 solo, y las operaciones más complejas, como raíces o logaritmos, ganaron todavía más.

Para 1980 eso significaba que una hoja de cálculo con miles de celdas numéricas, o un programa de diseño asistido, podía recalcular en segundos en lugar de minutos. Ese fue el argumento de venta del 8087 en estaciones de ingeniería y en las primeras versiones de software como Lotus 1-2-3.

El 8087 salió al mercado en 1980 como coprocesador opcional para el 8086 y el 8088. Foto de Albert Stoynov en Unsplash

El formato de 80 bits: así guarda los números el 8087

Internamente, el 8087 no trabaja con el float de 32 bits ni el double de 64 bits que usan los lenguajes modernos. Todas sus operaciones ocurren en un formato propio de 80 bits que Intel llamó temporary real, con más bits de significando que cualquier otro formato que el chip exponía hacia el programador.

Ese formato extendido absorbe el error de redondeo de cálculos intermedios antes de que el resultado final se convierta a 32 o 64 bits. Por eso una secuencia larga de sumas y restas en el 8087 pierde menos precisión que la misma secuencia hecha directamente en formato corto, ya que cada paso intermedio conserva bits de sobra que solo se descartan al final de la cadena.

Formato Bits totales Bits de significando Rango aproximado
Short real 32 24 supera 10⁻³⁶ a 10³⁸
Long real 64 53 mucho mayor que el short real
Temporary real 80 64 el más amplio de los tres, solo interno al chip

Cómo el 8087 alinea y suma dos números en punto flotante

Un número en punto flotante se guarda como signo por significando por 2 elevado al exponente. El significando sigue la forma 1.bbb…, un 1 inicial seguido del punto binario y el resto de los bits. Así, el binario 1.1 representa 1 + 1/2, es decir 1.5 en decimal, y 1.0011 representa 1 + 1/8 + 1/16, o 1.1875.

Para sumar dos números hace falta igualar sus exponentes antes de sumar los significandos, igual que alinear las comas decimales de 13.5 y 3.5 antes de sumarlas a mano. El 8087 expresa 13.5 como 1.6875 por 2 al cubo y 3.5 como 1.75 por 2 a la uno. La diferencia de exponentes es 2, así que el chip desplaza el significando de 3.5 dos posiciones a la derecha antes de sumar.

flowchart TD
    A["Tomar los dos operandos"] --> B["Comparar exponentes"]
    B --> C["Desplazar el significando menor"]
    C --> D["Sumar los significandos alineados"]
    D --> E{"Hubo acarreo extra?"}
    E -->|"sí"| F["Desplazar 1 bit a la derecha y sumar 1 al exponente"]
    E -->|"no"| G["Resultado ya normalizado"]
    F --> G

La suma de los significandos ya alineados da, en este caso, un resultado que desborda un bit hacia la izquierda. El 8087 normaliza desplazando ese resultado una posición a la derecha y sumando 1 al exponente, hasta que el bit más significativo vuelve a ser un 1. El resultado final es 17.0, igual que sumar 13.5 más 3.5 a mano.

Código: simulando la alineación y la suma como el 8087

El mismo algoritmo de alinear, sumar y normalizar se reproduce en pocas líneas de Python usando math.frexp, que separa un número en significando y exponente, y math.ldexp, que los recompone.

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))

La salida es 17.0, el mismo resultado que calculó el 8087 en el ejemplo de alineación de más arriba.

Cómo funciona la resta: cancelación y normalización a la izquierda

Restar es más delicado que sumar. El resultado puede ser positivo, negativo o exactamente cero, y cuando dos números son parecidos, el resultado queda mucho más chico que ambos operandos. El 8087 alinea los exponentes igual que en la suma, pero después de restar los significandos, el bit más significativo casi nunca queda en la posición correcta, así que la normalización suele exigir desplazar varios bits a la izquierda.

flowchart TD
    A["Alinear exponentes"] --> B["Restar los significandos"]
    B --> C{"El resultado es cero?"}
    C -->|"sí"| D["Devolver +0 o -0 según el signo"]
    C -->|"no"| E{"El significando es negativo?"}
    E -->|"sí"| F["Hacerlo positivo e invertir el signo del resultado"]
    E -->|"no"| G["Normalizar desplazando a la izquierda"]
    F --> G

Tomemos 8 menos 7.5. El chip expresa 8 como 1.0 por 2 al cubo y 7.5 como 1.875 por 2 al cuadrado; alineados, resta 1.0 menos 0.9375 y obtiene 0.0625. Para normalizar ese resultado hasta que el bit líder sea 1, el 8087 desplaza cuatro posiciones a la izquierda y resta 4 al exponente, llegando a 1.0 por 2 a la menos uno, o sea 0.5.

Si el significando de la resta queda negativo, el 8087 lo convierte a positivo e invierte el signo del resultado. Y si la resta da exactamente cero, no hay nada que normalizar: el chip devuelve directamente +0 o -0 según el signo que corresponda.

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))

La salida es (0.5, 4): el resultado correcto y los cuatro bits de desplazamiento que predice el análisis del die, calculados con la misma lógica de alinear, restar y normalizar.

Cómo probarlo

Los dos ejemplos corren con Python 3 puro, sin instalar nada adicional: la librería math es parte de la biblioteca estándar. Guardá cualquiera de los bloques en un archivo align_8087.py y ejecutalo con python3 align_8087.py en Windows, macOS o Linux; los tres sistemas traen o permiten instalar Python 3 desde la misma distribución oficial.

Dentro del datapath: sumador, shifter y conversor de exponente

flowchart TD
    A["Registro B"] --> C["Sumador entero"]
    D["Registros temporales o pila"] --> C
    C --> E["Registro de suma"]
    E --> F["Shifter"]
    F --> G["Normalización"]
    H["Contador de ceros a la izquierda"] --> F
    I["Conversor de exponente"] --> G

El datapath del 8087 ocupa la mitad inferior del die y agrupa los bloques que hacen el trabajo pesado. El sumador entero, en el centro, recibe un operando del registro B y otro de los registros temporales o de la pila de ocho registros del chip; su salida va al registro de suma.

El shifter es, según el análisis del die, el bloque que más espacio ocupa dentro de esa mitad del chip, porque su circuitería permite mover un valor una cantidad arbitraria de posiciones en un solo paso, en lugar de desplazar bit por bit. La operación de alinear significandos, descrita más arriba, depende enteramente de este componente.

El shifter del 8087 puede desplazar un valor una cantidad arbitraria de bits en un único paso. Foto de Alexandre Debiève en Unsplash

Junto al shifter trabaja un contador de ceros a la izquierda, que cuenta cuántos bits o bytes sobran del lado izquierdo de un valor para saber cuánto hay que desplazarlo durante la normalización. El conversor de exponente, por su parte, procesa los exponentes de ambos operandos, detecta cuándo un resultado se desborda o se queda sin rango, y ajusta el exponente final.

💭 Clave: El 8087 no gana velocidad solo por tener más transistores: gana porque reemplaza un bucle de software, desplazar bit por bit, por un circuito que resuelve el desplazamiento completo en un solo ciclo.

Casos especiales: infinitos, ceros y signos

Más allá de la aritmética normal, el microcódigo del 8087 dedica buena parte de sus 1.648 microinstrucciones a resolver casos límite. Sumar infinito más un número finito da infinito; sumar infinito positivo e infinito negativo no tiene un resultado numérico válido, así que el chip devuelve un valor especial llamado NaN, de not a number.

Restar dos infinitos iguales en signo también es indeterminado, y el resultado también es NaN. Estos casos no se resuelven con el sumador: el chip los detecta antes, comparando los bits de exponente y significando que identifican a un infinito o un NaN, y salta directo al resultado correcto sin pasar por la lógica de alineación.

El caso del cero con signo es más sutil. Si una resta da exactamente cero, el 8087 no puede normalizar un valor vacío, así que devuelve +0 o -0 según el signo que hubiera dado el cálculo. Esa distinción importa en operaciones posteriores, como dividir por el resultado: 1 dividido por +0 da +infinito, pero 1 dividido por -0 da -infinito.

Comparativa: del software al 8087 y al x87 moderno

La forma de resolver una suma en punto flotante cambió varias veces desde 1980. Esta tabla resume las opciones principales y cuándo tenía sentido cada una.

Opción Cuándo se usaba Ventaja Limitación
Software en el 8086 Sistemas sin coprocesador, código solo con enteros No necesita hardware adicional Cada suma en punto flotante cuesta decenas o cientos de ciclos
Coprocesador 8087 PC de IBM y clones con zócalo para el chip aparte Alineación y normalización en circuitería dedicada Chip extra, zócalo propio y mayor consumo
x87 integrado desde el 486DX CPU x86 de los 90 y 2000 con registros de 80 bits Mismo set de instrucciones, sin chip aparte Pila de registros rígida, difícil de optimizar por el compilador
SSE2 y AVX Código x86-64 moderno con precisión simple o doble Registros planos, vectoriza varias operaciones a la vez No ofrece la precisión extendida de 80 bits del x87 original

Casos de uso reales

Lotus 1-2-3 y las primeras hojas de cálculo para IBM PC recomendaban el 8087 para recalcular miles de celdas con decimales sin trabar la máquina. Los programas de diseño asistido por computadora de la época, que multiplicaban matrices para proyectar superficies en 3D, dependían todavía más del coprocesador: cada vista en perspectiva implicaba miles de sumas y multiplicaciones en punto flotante.

Las estaciones de ingeniería y los primeros paquetes de simulación numérica también vendían el 8087 como upgrade casi obligatorio. Para 1985, el sucesor 80287 venía integrado de fábrica en varias placas basadas en 80286, señal de que el mercado ya daba por sentado tener un coprocesador matemático a bordo.

Errores comunes y gotchas

  • Suponer que restar es simétrico a sumar: la resta debe manejar signo negativo y el caso de resultado cero, algo que la suma nunca enfrenta.
  • Ignorar la cancelación catastrófica: restar dos números muy parecidos deja pocos bits significativos en el resultado, aunque la operación parezca trivial.
  • Pensar que el x87 sigue siendo el camino rápido: los compiladores modernos para x86-64 prefieren SSE2 o AVX, y reservan el x87 casi solo para el tipo long double de C.
  • Olvidar los casos de infinito y NaN al validar resultados: una suma con un operando infinito no dispara una excepción, devuelve otro infinito o un NaN según el caso.
  • Asumir que el desplazamiento es gratis: el shifter del 8087 lo resuelve en un solo paso, pero el software que lo emula bit por bit paga un costo que crece con el tamaño del desplazamiento.

Profundizando: por qué el shifter ocupa tanto espacio en el die

Un shifter bit a bit, que mueve una posición por ciclo, es barato en transistores pero lento cuando hay que desplazar los 64 bits del significando completos. El 8087 resuelve esto con un shifter tipo barril: varias capas de multiplexores donde cada capa decide si desplaza el valor una cantidad fija de posiciones (1, 2, 4, 8…), y la combinación de capas permite cualquier desplazamiento en un solo paso.

Ese diseño cuesta área de silicio, porque cada capa de multiplexores necesita tantas celdas como bits tiene el dato, multiplicadas por el número de capas. Ese costo explica por qué, dentro del datapath, el bloque del shifter resulta más grande que el del sumador entero, aunque el sumador haga aritmética en apariencia más compleja.

El contador de ceros a la izquierda sigue una lógica parecida: en vez de revisar bit por bit desde la izquierda hasta encontrar el primer 1, usa un árbol de comparadores que reduce la búsqueda a unos pocos niveles lógicos. Sin ese circuito, cada normalización de una resta con cancelación numérica tardaría en proporción al tamaño del desplazamiento, algo que el chip no podía permitirse si quería mantener su ventaja de velocidad frente al 8086.

📖 Resumen en Telegram: Ver resumen

Tu próximo paso: corré align_and_sub con otro par de números cercanos, como 100.25 y 99.75, y confirmá a mano cuántos bits de desplazamiento predice la normalización antes de ejecutar el código.

📬 Recibí lo nuevo en tu email

Te avisamos de artículos grandes (1-2 por mes).

Preguntas frecuentes

¿Qué hace distinto al microcódigo del 8087 frente al software?

El microcódigo del 8087 corre dentro del chip y controla circuitería dedicada (sumador, shifter, conversor de exponente) en paralelo. Una rutina de software en el 8086 ejecuta las mismas alineaciones y normalizaciones con instrucciones enteras genéricas, paso por paso.

¿Por qué el coprocesador matemático 8087 necesita un shifter tan grande?

Porque alinear exponentes y normalizar resultados exige desplazar significandos de hasta 64 bits. Hacerlo en un solo ciclo, en vez de bit por bit, es lo que le da al chip su ventaja de velocidad frente a emular la operación en software.

¿Qué pasa si una resta en el 8087 da como resultado cero?

El chip no tiene nada que normalizar, así que devuelve directamente el valor especial +0 o -0, según el signo que hubiera correspondido al resultado antes de simplificarlo.

¿El FPU del 8087 maneja infinitos y valores NaN?

Sí: el 8087 detecta estos casos comparando los bits de exponente y significando antes de entrar a la lógica de suma o resta, y devuelve directamente infinito o NaN sin pasar por el sumador ni el shifter.

¿Sigue usándose el conjunto de instrucciones heredado del 8087 en CPUs actuales?

El set x87 sigue presente en los procesadores x86-64 por compatibilidad, pero los compiladores modernos prefieren SSE2 o AVX para punto flotante y reservan el x87 casi solo para el tipo long double de C.

Referencias

  • Righto.com: análisis del die del Intel 8087 y su microcódigo de suma y resta en punto flotante.
  • Wikipedia: historia y especificaciones del coprocesador matemático Intel 8087.
  • Wikipedia: estándar IEEE 754 para la representación de números en punto flotante.
  • Wikipedia: formato extendido de 80 bits usado por el x87, heredado del 8087.

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

Imagen destacada: Foto de Daniel Pantu en Unsplash

¿Te sirvió? ¿Te dio otro error? Contalo abajo: las preguntas se responden y le sirven al siguiente que llegue.

Dejar un comentario

Javier Alarcón

Ingeniero de infraestructura especializado en redes, sistemas Linux, Kubernetes y arquitecturas cloud. Cubre hardware, networking, observabilidad y prácticas de ingeniería para equipos de producción.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Podés incluir código entre <code>…</code> o, para varias líneas, <pre><code>…</code></pre>.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.