← Volver al blogSistemas de numeración

Coma flotante IEEE 754: de bits a números

Decodifica binary32 y binary64 de IEEE 754: signo, exponente sesgado, campo final del significando, valores especiales, redondeo y aritmética no asociativa.

DennyDenny
10 min de lectura
Chalkboard feature illustration for “Coma flotante IEEE 754: de bits a números”

Resumen: Para un valor binario normal, IEEE 754 usa (−1)s(1+f)2e−bias(-1)^s(1+f)2^{e-bias}: un bit de signo, un exponente sesgado y un campo de significando final con un 1 inicial implícito. Los patrones de exponente reservados codifican cero con signo, subnormales, infinitos y NaN. Como la mayoría de los resultados reales exactos deben redondearse a un formato finito, la suma y la multiplicación de coma flotante no son asociativas en general.

El binario de punto fijo funciona bien cuando se conocen el rango y la escala. Se vuelve incómodo cuando un cálculo debe abarcar valores separados por muchos órdenes de magnitud. Por ejemplo, un valor de punto fijo con signo de 32 bits y 16 bits fraccionarios tiene un paso de 2−162^{-16} y un rango aproximado de −32,768-32{,}768 a 32,767.9999832{,}767.99998.

La coma flotante asigna bits a un exponente, lo que permite que el punto radix se desplace con la magnitud. IEEE 754 se publicó por primera vez en 1985; la revisión vigente es IEEE 754-2019. Define formatos binarios y decimales, operaciones, redondeo, excepciones y comportamiento de intercambio. Una implementación puede ser hardware, software o una combinación de ambos, por lo que la conformidad no implica que cada CPU incluya todos los formatos en silicio.

¿Por qué usar coma flotante?

La notación científica resuelve el mismo problema sobre el papel: 6.674×10−116.674 \times 10^{-11} separa un significando pequeño de un exponente grande. IEEE 754 es notación científica binaria con reglas estrictas sobre cuántos bits recibe cada parte y cómo se codifican.

Dos presiones de diseño dan forma al formato:

  1. Codificaciones y operaciones especificadas. No existe un único patrón de bits por valor matemático: el cero tiene dos signos y los NaN tienen muchas codificaciones de carga útil. El estándar define cómo los tratan las operaciones y las comparaciones.
  2. Aritmética implementable. La comparación de exponentes y la alineación de significandos deben corresponderse con algoritmos finitos de hardware o software, con un comportamiento de redondeo y excepciones especificado.

Las concesiones adoptadas para conseguir ambas cosas —exponentes sesgados, bits iniciales implícitos y códigos de exponente para fines especiales— dan a IEEE 754 su forma característica.

El diseño de precisión simple (32 bits)

| 31 | 30 ............ 23 | 22 ........................... 0 |
|  s |       e (8 bits)   |          m (23 bits)             |
| 1  |                    |                                  |
  • Bit de signo ss: 1 bit. 0 significa no negativo; 1 significa negativo.
  • Exponente ee: 8 bits, almacenados como un entero sin signo con un sesgo de 127.
  • Campo de significando final mm: 23 bits. Para los números normales almacena la fracción posterior a un 1 inicial implícito.

Para los números normalizados (el caso habitual), el valor es:

v=(−1)s⋅(1+m223)⋅2e−127v = (-1)^s \cdot \left(1 + \frac{m}{2^{23}}\right) \cdot 2^{e - 127}

El «1.1.» del significando es implícito: el formato ahorra un bit aprovechando que todo número binario normalizado puede escribirse con un 1 inicial mediante la elección del exponente.

El diseño de precisión doble (64 bits)

La misma estructura, con campos más anchos:

| 63 | 62 ........... 52 | 51 ............................. 0 |
|  s |     e (11 bits)   |          m (52 bits)              |
  • Bit de signo ss: 1 bit.
  • Exponente ee: 11 bits, sesgo 1023.
  • Campo de significando final mm: 52 bits.

Valor (normalizado):

v=(−1)s⋅(1+m252)⋅2e−1023v = (-1)^s \cdot \left(1 + \frac{m}{2^{52}}\right) \cdot 2^{e - 1023}

Rango: aproximadamente ±1.8×10308\pm 1.8 \times 10^{308}, con unas 15-17 cifras decimales significativas.

¿Por qué sesgar el exponente?

Almacenar el exponente como e−biase - bias en vez de en complemento a dos aporta una ventaja sutil pero importante: la comparación de coma flotante se reduce a una comparación de enteros sobre el patrón de bits (con un cambio en el bit de signo). Si se comparan dos valores de coma flotante no negativos como enteros sin signo de 32 bits, el valor de coma flotante mayor es el entero mayor. Los exponentes en complemento a dos romperían esta relación monótona en la frontera entre exponentes positivos y negativos.

El campo de exponente formado por todos ceros se reserva para ceros y subnormales, mientras que el formado por todos unos se reserva para infinitos y NaN. Para binary32, los campos de exponente normales del 1 al 254 corresponden a exponentes no sesgados de −126-126 a +127+127.

Los casos especiales

Bits del exponenteBits del significando finalSignificado
Todo cerosTodo ceros±0\pm 0 (el bit de signo determina cuál)
Todo cerosDistinto de ceroSubnormal
Cualquier valor intermedioCualquieraNúmero normalizado
Todo unosTodo ceros±∞\pm \infty
Todo unosDistinto de ceroNaN (no es un número)

El cero tiene dos codificaciones (+0+0 y −0-0). Se comparan como iguales en una comparación IEEE, pero producen resultados con signo distintos en algunas operaciones (por ejemplo, 1/+0=+∞1 / +0 = +\infty, 1/−0=−∞1 / -0 = -\infty).

Los subnormales (históricamente también llamados desnormales) cubren el hueco entre el cero y el número normal más pequeño. El 1 inicial implícito pasa a ser 0, y binary32 usa un exponente efectivo de −126-126. Esto proporciona subdesbordamiento gradual: el espaciado absoluto permanece constante en el rango subnormal, mientras que la precisión relativa disminuye al acercarse a cero.

El infinito resulta de un desbordamiento o de una división por cero. Se propaga por la aritmética de las formas obvias: ∞+1=∞\infty + 1 = \infty, ∞×0=NaN\infty \times 0 = \text{NaN}.

NaN señala una operación no válida: −1\sqrt{-1}, 0/00 / 0, ∞−∞\infty - \infty. NaN es contagioso: toda operación aritmética en la que interviene NaN produce NaN. Es fundamental que NaN≠NaN\text{NaN} \neq \text{NaN} en la comparación IEEE 754, por lo que las suites de pruebas usan isnan() en lugar de ==.

Ejemplo desarrollado: decodificar 0x40490FDB

Una famosa constante de 32 bits. En binario:

0x40490FDB = 0100 0000 0100 1001 0000 1111 1101 1011

Dividida en campos:

  • Signo: 0 — positivo
  • Exponente: 1000 0000 = 12810128_{10} — exponente real 128−127=1128 - 127 = 1
  • Significando final: 100 1001 0000 1111 1101 1011

El significando es 1.(trailing bits)21.\text{(trailing bits)}_2, es decir:

1.1001001000011111101101121.10010010000111111011011_2

Calculamos la fracción almacenada:

mfrac=0b10010010000111111011011223=4,788,1878,388,608≈0.5707963m_{frac} = \frac{0b10010010000111111011011}{2^{23}} = \frac{4{,}788{,}187}{8{,}388{,}608} \approx 0.5707963

Por tanto, el significando es 1.57079631.5707963. Multiplicamos por 212^1:

v≈3.1415927v \approx 3.1415927

Es π\pi con precisión simple. La constante 0x40490FDB es la codificación canónica IEEE 754 de precisión simple de π\pi y aparece en bibliotecas numéricas, sombreadores de GPU y preguntas de examen de todo el mundo.

Ejemplo desarrollado: codificar 5.75

Convierte 5.755.75 a IEEE 754 de precisión simple.

Paso 1: convertir a binario. 5=10125 = 101_2. 0.75=0.5+0.25=0.1120.75 = 0.5 + 0.25 = 0.11_2. En conjunto: 5.75=101.1125.75 = 101.11_2.

Paso 2: normalizar. Desplaza el punto binario a la izquierda hasta que quede exactamente un dígito a su izquierda:

101.112=1.01112×22101.11_2 = 1.0111_2 \times 2^2

Por tanto, el exponente real es 22.

Paso 3: codificar cada campo.

  • Signo: positivo, por tanto s=0s = 0.
  • Exponente: real 22 + sesgo 127127 = 129129 = 1000 0001.
  • Significando final: elimina el 1 inicial y almacena los siguientes 23 bits: 0111 seguido de 19 ceros, o 01110000 00000000 0000000.

Paso 4: ensamblar.

0 | 10000001 | 01110000000000000000000

En hexadecimal: 0x40B80000. Puedes comprobarlo invirtiendo el procedimiento de decodificación sobre este patrón y recuperando exactamente 5.75.

Observa que 5.75 tiene una representación binaria exacta: su parte fraccionaria es una suma de potencias negativas de dos (2−1+2−22^{-1} + 2^{-2}). La mayoría de las fracciones decimales no la tienen. 0.10.1 en precisión simple es 0x3DCCCCCD, que se decodifica de vuelta como 0.1000000014901...0.1000000014901..., con un error de aproximadamente 10−910^{-9}. Ese diminuto error es la causa fundamental de cada sorpresa de «0.1+0.2≠0.30.1 + 0.2 \neq 0.3» con la que se encuentran quienes empiezan.

Direcciones de redondeo

IEEE 754-2019 denomina cinco atributos de dirección de redondeo:

  • Redondear al más cercano, empates a par (predeterminado). Redondea al valor representable más cercano; en caso de empate, elige el valor cuyo bit menos significativo del significando sea 0.
  • Redondear al más cercano, empates alejándose de cero. Este atributo se asocia especialmente con la aritmética decimal y no está expuesto por todos los entornos de coma flotante binaria.
  • Redondear hacia cero (truncamiento).
  • Redondear hacia +∞+\infty (techo).
  • Redondear hacia −∞-\infty (suelo).

El modo predeterminado minimiza el sesgo estadístico: el simple «redondeo de mitades hacia arriba» introduce un ligero sesgo al alza cuando se acumulan muchos valores, mientras que el redondeo de mitades a par se compensa en promedio.

Las direcciones que puede seleccionar una aplicación dependen de su lenguaje, entorno de ejecución, compilador y entorno de hardware. La reproducibilidad también depende del orden de evaluación, la precisión intermedia, las operaciones fusionadas, las funciones de biblioteca y el tratamiento de subnormales, no solo de la dirección de redondeo elegida.

Por qué la aritmética de coma flotante no es asociativa

Considera tres valores IEEE 754 de precisión simple: a=1020,b=−1020,c=1a = 10^{20}, b = -10^{20}, c = 1.

(a+b)+c(a + b) + c: primero, a+b=0a + b = 0 exactamente (la cancelación es limpia). Después, 0+c=10 + c = 1. Resultado final: 11.

a+(b+c)a + (b + c): primero, b+cb + c. El exponente de bb es aproximadamente 67; el exponente de cc es 0. Al alinear los significandos, cc se desplaza mucho más allá de la precisión de binary32. Después del redondeo, b+c=bb+c=b. Entonces a+b=0a+b=0.

Las dos parentizaciones producen respuestas diferentes, por lo que la suma de coma flotante no es asociativa. Que un compilador pueda reasociar expresiones depende de las reglas del lenguaje, la implementación, las opciones de optimización y el entorno de coma flotante. El código sensible a la reproducibilidad debe documentar esas elecciones y probar la cadena de herramientas real, en vez de confiar en una sola opción del compilador como garantía universal.

La misma propiedad se aplica a la resta (casos de cancelación), la multiplicación (productos extremadamente pequeños pueden subdesbordarse a cero) y la división (las divisiones por números muy pequeños pueden desbordarse a infinito).

Hardware: la FPU

Un sumador de coma flotante de hardware suele realizar etapas como estas; los procesadores sin FPU pueden implementar la misma semántica en software:

  1. Decodificar los campos de ambos operandos.
  2. Comparar los exponentes; desplazar a la derecha el significando con el exponente menor según la diferencia, conservando un bit de guarda, de redondeo y pegajoso para el redondeo.
  3. Sumar o restar los significandos alineados (mediante un sumador de anticipación de acarreo ancho).
  4. Normalizar el resultado desplazándolo a la izquierda hasta que aparezca un 1 inicial; ajustar el exponente en consecuencia.
  5. Redondear según el modo de redondeo activo.
  6. Codificar el resultado en formato IEEE 754.

La ruta de datos necesita comparación de exponentes, alineación de significandos, lógica de suma/resta, detección de ceros iniciales, normalización, redondeo, tratamiento de casos especiales e informe de excepciones. La extensión F de RISC-V define instrucciones binary32 y D añade binary64; ambas son opcionales, lo que ilustra la distinción entre un formato estándar y hardware obligatorio.

La ruta del significando reutiliza técnicas de aritmética binaria, pero una FPU es más que una ALU de enteros con componentes adicionales: el redondeo, el subdesbordamiento gradual, los NaN, los infinitos, los ceros con signo y las excepciones forman parte de su comportamiento arquitectónico.

Formatos más amplios

Además de la precisión simple y doble, IEEE 754 define:

  • Precisión media (binary16): 1 bit de signo + 5 de exponente + 10 de significando final. Se usa en gráficos y cargas de trabajo numéricas de precisión reducida.
  • Precisión cuádruple (128 bits): 1 + 15 + 112. Es poco común; se usa en análisis numérico y algunos cálculos financieros.
  • bfloat16 (no es un formato básico de intercambio de IEEE 754): 1 + 8 + 7. Conserva un rango de exponente similar al de binary32 con mucha menos precisión y se utiliza ampliamente en aprendizaje automático.

Cada variante equilibra rango y precisión para su carga de trabajo objetivo.

Errores habituales

  • Usar == sin un modelo de error. La comparación exacta es correcta para algunas invariantes y valores discretos; los resultados aproximados necesitan una tolerancia absoluta/relativa específica del dominio o una política de unidades en el último lugar.
  • Olvidar que NaN != NaN. Usa isnan() para detectar NaN.
  • Suponer asociatividad. Reordenar sumas puede cambiar la respuesta. Las opciones del compilador pueden habilitar reasociaciones inseguras; entiéndelas antes de usarlas.
  • Mezclar intuiciones decimales y binarias. 0.10.1 no tiene representación binaria finita exacta. Para reglas de negocio decimales, considera enteros escalados, coma flotante decimal o BCD, y aun así define el redondeo en cada límite necesario.
  • Suponer el rendimiento de los subnormales. Algunos procesadores manejan los subnormales a plena velocidad; otros penalizan su uso o admiten modos de vaciado a cero. Mide en el objetivo y conserva el subdesbordamiento gradual cuando la corrección lo requiera.

Conexión con una arquitectura de CPU más amplia

La organización de registros es específica de cada ISA. Algunas arquitecturas usan bancos de registros enteros y de coma flotante separados; otras superponen o unifican partes del estado. Las cargas, los almacenamientos, las conversiones, los movimientos y las convenciones de llamada definen la frontera.

Para la aritmética más amplia, la parte entera se basa en estructuras relacionadas con el sumador de 8 bits, escaladas y optimizadas para su objetivo. La parte de coma flotante añade alineación, normalización, redondeo, excepciones y tratamiento de valores especiales.

Próximos pasos

Continúa con El complemento a dos explicado para los enteros con signo de ancho fijo y, después, con BCD: fundamentos del decimal codificado en binario para la codificación decimal orientada a dígitos.

Para experimentar a nivel de bits, utiliza una ruta de datos de tipo ALU para la aritmética de significandos; después modela la alineación, la normalización, los bits de guarda/redondeo/pegajoso y los casos especiales como etapas separadas.

La referencia normativa es el vigente estándar IEEE 754-2019 para aritmética de coma flotante.

Convierte la idea en un circuito funcional.