9.4.1 Representación interna
Numeros en coma flotante:
El programa es una versión simplificada del Conversor automático de formatos ( 2.2.4a) incluido al tratar de la representación interna de cantidades numéricas de coma flotante. Esta versión solo muestra la representación de números en simple precisión (32 bits), aunque puede ser fácilmente adaptado para representar números de doble precisión (64 bits).
Como entrada puede introducirse un número cualquiera, incluso en notación científica. Como salida se muestra el formato de almacenamiento interno según el estándar IEEE 754 para números de simple precisión. Primero el bit de signo; a continuación el exponente, y finalmente la mantisa. Naturalmente es necesario que el equipo utilice realmente este formato para su almacenamiento interno (lo que es prácticamente seguro en la informática actual).
#include <iostream> //
Representación IEEE 754
#include <conio.h>
#include <values.h>
using namespace std;
void map(float); // mostrar patron de bits del número introducido
int main() {
// ====================
double uDouble = 0;
while (true) {
// bucle principal
cout << "\r\nIntroduzca un numero (0 para terminar)";
cout << "\r\npuede usar notacion cientifica. xEy :" << endl;
cin >> uDouble;
if (uDouble == 0) break;
cout << "\r\nValor seleccionado: " << uDouble << endl;
double aDouble;
if (uDouble < 0) aDouble = -uDouble;
else aDouble = uDouble;
if (aDouble > MAXFLOAT) {
cout << "\r\nValor fuera de rango para float (32 bits)";
cout << "\r\nvalor maximo " << MAXFLOAT << endl;
}
if (aDouble < MINFLOAT) {
cout << "\r\nValor fuera de rango para float (32 bits)";
cout << "\r\nvalor minimo " << MINFLOAT << endl;
}
map((float) uDouble);
// invocar la subrutina de salida
}
return EXIT_SUCCESS;
}
void map (float val) { //
Mostrar patron de bits
cout << "\r\n\n\ Signo Exp. Mantisa\r\n";
cout << "Rep. IEEE754: ";
float* pflo = &val;
char* pchar = reinterpret_cast<char*> (pflo);
// suponemos corriendo el programa en un sistema Little-endian (p.e. Windows)
unsigned char value = *(pchar+3); // empezamos por el byte de la derecha
for(int i = 7; i >= 0; i--) {
cout << ((value & (1 << i)) ? "1" : "0");
if (i == 7) cout << " ";
// separador entre bit de signo y exponente
}
value = *(pchar+2);
for( i = 7; i >= 0; i--) {
cout << ((value & (1 << i)) ? "1" : "0");
if (i == 7) cout << " ";
// separar bits de exponente y mantisa
}
value = *(pchar+1);
for( i = 7; i >= 0; i--) cout << ((value & (1 << i)) ? "1" : "0");
value = *pchar;
for( i = 7; i >= 0; i--) cout << ((value & (1 << i)) ? "1" : "0");
cout << "\r\n";
}
Resultados obtenidos con diversas entradas:
Introduzca un numero (0 para terminar)
puede usar notacion cientifica. xxEy :
4.363435e-35
Valor seleccionado: 4.36344e-35
Signo
Exp. Mantisa
Rep. IEEE754: 0 00001100 11010000000000000000000
-23040
Valor seleccionado: -23040
Signo
Exp. Mantisa
Rep. IEEE754: 1 10001101 01101000000000000000000
4.77544580e-39
Valor seleccionado: 4.77545e-39
Valor fuera de rango para float (32 bits)
valor minimo 1.17549e-38
Signo Exp. Mantisa
Rep. IEEE754: 0 00000000 01101000000000000000000
Comentario:
El cuerpo de la función main se limita a aceptar una
entrada y enviar el número a la rutina de presentación. Si los valores
están fuera del rango permitido para un float (32 bits), se muestra un
mensaje de aviso, aunque continúa el proceso. El código está
optimizado para el compilador Borland C++ Builder. En caso de utilizarse
otro, por ejemplo, GNU Cpp para Windows (versión
Mingw), es posible que se presenten errores con las constantes manifiestas MINFLOAT
y MAXFLOAT
. En cuyo caso
pueden suprimirse las sentencias de comprobación o colocar los valores adecuados.
La función map
que muestra el patrón de bits, supone que utilizamos una máquina "Little-endian"
( 2.2.6a),
como es el caso de sistemas Windows sobre plataformas Intel. En estos
casos, los 4 octetos del número están almacenados en orden inverso.
Suponiendo un orden creciente de posiciones de memoria, el que
consideramos bit más significativo, es el último. Por ejemplo, la posición real de
los octetos del primer ejemplo es el siguiente:
00000000 00000000 01101000 00000110
El valor 0/1 de cada bit individual se muestra mediante una expresión que
utiliza el AND entre bits (bitand):
cout << ((value & (1 << i)) ? "1" : "0"); // L.0
Como las operaciones de manejo de bits exigen operandos de tipo entero ( 4.9.3), se recurre a un pequeño truco, ya que no es posible utilizar directamente el float val que contiene el número cuya radiografía interna estamos realizando:
float* pflo = &val;
// L.1
char* pchar = reinterpret_cast<char*> (pflo);
//
L.2
unsigned char value = *(pchar+3);
// L:3
En primer lugar se construye un puntero pflo
que contiene la dirección de inicio de val
(L.1).
A continuación creamos un puntero-a-char, pchar
,
que señala al mismo sitio (L.2). Para esto utilizamos un modelado por
la fuerza bruta ( 4.9.9d).
Finalmente creamos un char value (de 8 bits), por el procedimiento de suponer que empieza en la dirección del
puntero pchar
(L.3). Observe que aquí jugamos con el desplazamiento +3, +2, etc. respecto de la posición
inicial, para seleccionar el octeto correspondiente de val. que queremos analizar en cada bucle.
Observe que el compilador realiza automáticamente una última conversión; es la necesaria para transformar el char value a un tipo entero (int) tal como se necesita en la operación AND de la sentencias L.0.