Ken Shirriff reconstruyó el cálculo de la tangente en el coprocesador de coma flotante Intel 8087. Intel presentó el chip en 1980 para el IBM PC y otros sistemas. El 8087 calculaba una tangente en unos 90 microsegundos. La misma operación emulada por un 8086 requería aproximadamente 13.000 microsegundos.

FPTAN microcode
FPTAN:
#1039 st(0) -> tmpA store argument in tmpA
#1040 stackPtr-- check if room on stack to push result
#1041 stack overflow?
#1042 jmp #1022 if tmp empty/special/overflow/div exit if bad argument or stack overflow
#1043 jmp #1045 if tmpA:tag ZERO is argument 0?
#1044 except:precision precision exception except for 0
#1045 expconst 0x3ff0 Test if exponent >= -16
#1046 adder: tmpA:exp + 0 cin=1 argument exponent + 1
#1047 expConst -> Breg
#1048 adder: sumreg:frac - Breg cin=1 subtract -15
#1049 jmp #1061 if adder pos CORDIC if exponent >= -16
#1050 expconst 0x3fff non-CORDIC path:
#1051 tmpA:exp -> Breg check original exponent
#1052 expConst -> tmpB:frac against exponent 0
#1053 adder: tmpB:frac - Breg cin=1 subtract
#1054 sumreg:frac -> expConv 3fff - exp (i.e. -exp unbiased)

Shirriff retiró la tapa del chip con un cincel y examinó el die con un microscopio. La ROM de microcódigo situada en el centro contiene 1.648 microinstrucciones de 16 bits. La zona inferior del die reúne el circuito de datos de coma flotante, la ROM de exponentes, la ROM de constantes, el desplazador, el sumador, los registros de pila, los registros temporales y un registro de desplazamiento de 16 bits para las decisiones CORDIC.

Jack Volder desarrolló CORDIC en 1956 para el ordenador digital de navegación del Convair B-58 Hustler. El método representa un ángulo mediante un vector y aplica rotaciones cuyos valores de tangente son potencias de dos. Cada rotación necesita desplazamientos, sumas y restas, mientras que los ángulos proceden de una tabla almacenada. El ordenador CORDIC-II del B-58 era una máquina serie de 30 bits a 198,4 kHz. Pesaba 134 libras, tenía 2.498 transistores y 4.265 diodos, y almacenaba unos 39 kilobytes en una memoria de tambor. Una operación CORDIC tardaba 5 milisegundos.

El 8087 utiliza una variante que selecciona o descarta cada ángulo de la tabla. Primero reduce el ángulo de entrada con hasta 16 pasos CORDIC. Esa fase proporciona unos 16 bits de precisión. Para el pequeño ángulo restante, el chip emplea la aproximación racional 3x/(3-x2). Su error crece proporcionalmente a x4. Con un residuo inferior a 2-16, el error queda por debajo de 2-64. El procesador conserva numerador y denominador como coordenadas separadas del vector y evita una división final. FPTAN devuelve X como denominador e Y como numerador. Intel llamó a este resultado Partial Tangent.

El microcódigo divide el trabajo en pseudo-división CORDIC, aproximación racional y pseudo-multiplicación CORDIC. Durante la pseudo-división compara el ángulo residual con cada valor almacenado y guarda un 1 o un 0 en el registro de desplazamiento. Para la entrada de 0,95 radianes usada en el análisis, la secuencia es [1,0,0,1,0,1,0,1,0,0,1,0,0,1,1,1]. Las rotaciones se aplican después en orden inverso. La más pequeña se ejecuta primero para reducir el error de redondeo. El vector deja de estar sobre el círculo unitario, pero Y/X sigue produciendo la tangente.

El microcódigo de FPTAN comienza en la dirección #1039. Primero comprueba los argumentos inválidos y un posible desbordamiento de la pila. Con un exponente de -64 o menor, devuelve el argumento sin cambios y coloca 1 como denominador. Con un exponente de -17 o menor, omite CORDIC y salta directamente a la aproximación racional. Los exponentes entre -1 y -16 siguen la ruta CORDIC. La documentación fija el rango de entrada en 0<θ<π/4. El código trata explícitamente el cero y parece funcionar con valores mayores, aunque pierde precisión al acercarse a π/2. El valor 0,95 usado en el ejemplo queda fuera del rango documentado.

La aproximación necesita calcular el cuadrado del ángulo residual. Para ello, el 8087 usa una multiplicación Booth radix 4, que procesa dos bits por paso. El hardware ejecuta 32 sumas dentro de un bucle controlado por microcódigo. La constante 3 procede de transistores especializados. Las demás operaciones se realizan con desplazamientos y sumas. La división costosa no llega a ejecutarse.

Internamente, el 8087 guarda los números en el formato Temporary Real de 80 bits. Incluye un bit de signo, un exponente de 15 bits y una significand de 64 bits. El sesgo del exponente es 16.383. Por eso, el exponente 1 se almacena como 0x4000 y el exponente -16 como 0x3fef. Los ocho registros de pila usan las etiquetas zero, valid, special y empty. tmpA y tmpB son registros de 80 bits. tmpC carece de signo, exponente y etiqueta. Su significand tiene 68 bits y añade bits de overflow, guard, round y sticky.

El microcódigo realiza cálculos parecidos a la aritmética de punto fijo entero. Los exponentes solo existen de forma implícita. Los desplazamientos mantienen los bits útiles dentro del ancho disponible mientras los valores disminuyen durante la pseudo-división y aumentan durante la pseudo-multiplicación. Algunas partes del circuito usan un desplazador de 68 bits y un sumador de 69 bits. Una suma o un desplazamiento suele requerir dos microinstrucciones. El registro B del sumador es independiente de tmpB.

Para la entrada 0,95, la pseudo-división CORDIC consume el 33 % del tiempo. La aproximación racional ocupa el 15 %, sobre todo por el cálculo del cuadrado. La pseudo-multiplicación representa el 47 %. El 5 % restante corresponde a tareas auxiliares. FPTAN necesita normalmente 450 ciclos de reloj, con una duración de entre 30 y 540 ciclos según la entrada.

Intel pasó a las aproximaciones polinómicas con el Pentium porque su multiplicador hacía práctico ese método. Las bibliotecas modernas de Intel, como MKL y SVML, utilizan polinomios junto con instrucciones SIMD. Las operaciones x87 y los valores de coma flotante de 80 bits tienen hoy un papel reducido en el software de 64 bits.

Shirriff realizó la reconstrucción con ayuda del Opcode Collective, especialmente Smartest Blob y Gloriouscow. El grupo convirtió imágenes de ROM en datos de microcódigo y empleó aprendizaje automático para clasificar las celdas de la ROM. Los datos del microcódigo del 8087 están disponibles en el repositorio granite de GitHub.