Especificación IEEE-754, para representar valores decimales en punto flotante de simple precisión. Ing. Cosme Rafael Marcano Gamero, MSc, DEA. cosmemarcano@gmail.com En este escrito se explica la especificación utilizada por la mayoría de los lenguajes de programación disponibles en el mercado para representación los así llamados números reales, en los dispositivos de almacenamiento, ya sea primario (RAM) o secundario (discos duros, etc.). La especificación IEEE-754 establece que los bits que ocupa un valor decimal, en formato de simple precisión, es la siguiente: Bit: bit de signo: = -, = + Bit-: 8 bits para el exponente, desplazado en 7. Bit-: bits para la mantisa normalizada. EJEMPLO. Tomemos como ejemplo a -. En primer lugar, la parte entera (sin signo) de -. es. Como no hay que mover la coma decimal, * exp onente 7 7 b Por último, como - es negativo, el bit de signo, es decir, el bit, será igual a. Entonces: B F 8 En vista de que los valores usualmente son almacenados terminando con el byte más significativo (lo cual es referido a veces como bigendian), entonces la representación de -, en formato IEEE-754 sería igual a BF8, y se colocaría en memoria o en archivos binarios como: 8BF, como se puede ver en la siguiente figura.
Figura. Representación en un archivo binario (reales.bin), de veintidós valores decimales en formato IEEE-754. En la Figura aparecen las representaciones de los siguientes valores decimales en formato IEEE-754: -, -.875, -.75, -.65, -.5, -.75, -.5, -.5,,.5,.5,.75,.5,.65,.75,.875,,.5,.5,.75,.7 y.45. Nótese que cada valor ocupa cuatro bytes, es decir, bits. Cada byte aparece representado en forma de dos dígitos hexadecimales. Así, por ejemplo, la representación de -., o sea, 8BF, corresponde a los cuatro primeros bytes a partir del inicio del archivo (posición ). EJEMPLO. Como otro ejemplo, hallemos la representación en formato IEEE-754 del penúltimo valor almacenado en este archivo, es decir,.7. En primer lugar, la parte entera de.7 es. Para normalizar este valor no hay que mover la coma decimal, entonces: * exp onente 7 7 b La conversión de la parte decimal (.7), viene a ser: Bit x i x * i,,,... acumulado *.5 *.5 * * 4.65.6875 5 *.6875
7 6 *.6875 * * 8.6955.699875 9 *.699875 *.699875 *.69977 *.699957 *.699957 4 *.699957 5 *.69998689 6 *.699996948 7 *.699996948 8 *.699996948 9 *.699998856 *.69999989 *.69999989 *.69999998 *.69999998 Parte decimal convertida:.69999998 Por último, como.7 es positivo, el bit de signo, es decir, el bit, será igual a. Entonces: F D 9 9 9 9 A Y se almacenará en el archivo con la siguiente apariencia: 9A 99 D9 F, como se muestra en la Figura. Nótese que en ambos ejemplos se omitió, de acuerdo a lo establecido en la especificación IEEE-754, la representación del., después de haberse normalizado la mantisa. Recuerde que cada bit de la representación de la parte decimal del valor que se desea convertir al formato IEEE-754, constituye un factor que multiplica a potencias negativas de dos. Esto es, si los bits de la parte decimal de.7 (que, en este caso es exactamente igual a la mantisa) son los siguientes:
Entonces, el equivalente decimal de esa representación será: 4 * * * *... * *.7 EJEMPLO. Por último, como un ejemplo de conversión de un valor cuya parte entera requiere más de un dígito binario, tomemos el.45, que es el último valor almacenado en el archivo reales.bin (ver Figura ). En este caso, la parte entera es igual a = b (7Bh). Para obtener la representación de la parte decimal, observamos que: Bit x i x * i,,,... acumulado *. *.5 *.75 * * 4 5.475.475 6 *.475 7 *.4455 8 *.449875 9 *.449875 * *.449875.44977 *.449957 *.449957 4 *.449957 5 *.44998689 6 *.4499899 7 *.4499899 Parte decimal convertida: =.4499899 Entonces:.45 =. Al normalizar, queda: 6.* exp onente 7 6 b Quitando el., y agregando la parte decimal, nos queda:
El bit de signo es, por ser.45 un valor positivo. 4 F 6 E 6 6 6 Finalmente, la representación de.45 en formato IEEE-754, que resultó ser igual a 4 F6 E6 66, es almacenado como 66 E6 F6 4, de acuerdo a lo mostrado en la Figura. COMENTARIOS SOBRE EL PROGRAMA QUE GENERÓ EL ARCHIO BIMARIO reales.bin Inclusión de la librería en donde se definen la clase de objeto #include <fstream> Definición de la estructura requerida para escribir y leer variables de tipo real (float) en/desde un archivo binario en C++: struct real { float valor; }; Declaración de un apuntador a la estructura requerida para escribir y leer variables de tipo real (float) en/desde un archivo binario en C++: real* preal = new real(); Declaración de una variable que contendrá el nombre del archivo binario en donde se almacenarán los valores reales: static const char *BINARIO = "reales.bin"; Apertura del archivo de escritura. Nóyese que se abrirá con el nombre salida. Con este nombre (manejador) se hará referencia al archivo físico reales.bin dentro del programa. Nótese también que se abre con los atributos ios_base::out para indicar que es un archivo de salida (sobre el cual se escribirán datos), ios_base::binary para indicar que los datos deben ser almacenados en formato binario, y el atributo ios_base::trunc, para indicar que los datos deben ser truncados (no redondeados) a la longitud exacta en bytes que ocupen. std::ofstream salida(binario, ios_base::out ios_base::binary ios_base::trunc); if (salida==null){ cout << "Error: no pude abrir Archivo de salida." << endl; return(-);
} Asignación del valor inicial de la variable real a ser escrita en el archivo binario. preal->valor=-.; for (int contador=; contador<; contador++){ // escribir el contenido de la variable en el archivo salida.write((const char*)preal, sizeof(real)); // mostrar en pantalla el mismo valor printf("\n%.7f", preal->valor); // incrementar en.5 el valor de la variable. Esto quiere decir que // el segundo valor a almacenar en el archivo será -.875, luego, - // -.75, etc. preal->valor+=.5; } preal->valor+=.; // aquí alcanza el valor de.7 printf("\n%.7f", preal->valor); salida.write((const char*)preal, sizeof(real)); // ejemplo con parte entera de más de un dígito binario preal->valor =.45; printf("\n%.7f", preal->valor); salida.write((const char*)preal, sizeof(real)); // cerrar archive explícitamente (aunque C++ cierra automáticamente los // archives que estén abiertos al finalizar la ejecución del programa). salida.close(); La lectura de variables reales desde un archivo binario se realiza de manera similar a la escritura y queda planteado como ejercicio. MOTA: Para poder utilizar printf("\n%.7f", preal->valor), se debe incluir el archivo de prototipos stdo.h el cual no es orioui de C++, sino de C estándar. Esta función, sin embargo, permite controlar a voluntad del programador (y las características del lenguaje de programación, por supuesto) el número de posiciones decimales que se quiere mostrar en pantalla. En este sentido, es más potente que la función std::cout de C++. Para una comparación más detallada entre printf()y std::cout se puede consultar el artículo Comentarios sobre printf()y std::cout m que está disponible en cosmemarcano.wordpress.com. COMENTARIO FINAL. Afortunadamente, la conversión de los números con decimales (también llamados números reales) es automática y transparente al programador en lenguaje de de alto nivel, como Fortran, Pascal y C. No obstante, a la hora de diseñar un prototipo de hardware que se desee conectar a una computadora u otro circuito digital estándar, es necesario respetar el formato utilizado para cada tipo de datos, de manera que se pueda compartir información entre tales dispositivos.