Hola Khabrovites. Me ha gustado mucho el tema de los registros de coma flotante durante mucho tiempo. Siempre me preocupó cómo la salida a la pantalla, etc. Recuerdo que hace mucho tiempo en la universidad estaba implementando mi clase de números de coma flotante que consta de 512 bits. Lo único que no pude comprender de ninguna manera fue la salida a la pantalla.
Tan pronto como tuve tiempo libre, tomé el viejo. Me conseguí una libreta y nos vamos. Quería pensar en todo, solo ocasionalmente mirando el estándar IEEE 754.
Y esto es lo que salió de todo. Para los interesados, pido gato.
Para dominar este artículo, necesita saber lo siguiente: qué es un bit, un sistema binario, aritmética a nivel de conocimiento de grados negativos. El artículo no afectará los detalles de ingeniería de la implementación a nivel de procesador, así como los números normalizados y desnormalizados. Se pone más énfasis en convertir un número en forma binaria y viceversa, así como en explicar cómo los números de coma flotante generalmente se almacenan en forma de bits.
Los números de punto flotante son una herramienta muy poderosa que necesita para poder usar correctamente. No son tan comunes como los registros enteros, pero tampoco son tan complejos si son penetrados de manera competente y lenta.
En el artículo de hoy, usaré registros de 32 bits como ejemplo. Los números de doble precisión (64 bits) funcionan exactamente con la misma lógica.
Primero, hablemos sobre cómo se almacenan los números de coma flotante. Los 31 bits más antiguos son significativos. Un solo significa que el número es negativo, y cero, respectivamente, es lo contrario. Luego vienen 8 bits del exponente. Estos 8 bits son el número sin signo habitual. Y al final hay 23 bits de la mantisa. Por conveniencia, denotamos el signo como S, el exponente como E y la mantisa, curiosamente, M.
Obtenemos la fórmula general
La mantisa se considera un solo bit implícito. Es decir, la mantisa será de 24 bits, pero dado que el bit 23 más alto siempre es uno, no puede escribirlo. Por lo tanto, esta "restricción" nos dará la singularidad de representar cualquier número.
Mantissa es un número binario ordinario, pero a diferencia de los enteros, el bit más significativo es 2 ^ 0 grados y luego en grados decrecientes. Aquí es donde el expositor es útil. Dependiendo de su valor, la potencia del bit alto dos aumenta o disminuye. Ese es todo el genio de esta idea.
Tratemos de mostrar esto con un buen ejemplo:
Imagine el número 3.625 en forma binaria. Primero, dividimos este número en potencias de dos.
El grado de los dos mayores es igual a uno. E - 127 = 1. E = 128.
0 1,000,000 1,101,000,000,000,000,000,000
Ese es todo nuestro número.
Probemos también en la dirección opuesta. Supongamos que tenemos 32 bits, 32 bits arbitrarios.
0 10000100 (1) 11011100101000000000000
El mismo bit implícito de alto orden se indica entre paréntesis.
Primero, calcula el exponente. E = 132. En consecuencia, el grado de los dos mayores será igual a 5. Total tenemos el siguiente número:
Es fácil adivinar que solo podemos almacenar un rango de 24 grados dos. En consecuencia, si dos números difieren exponencialmente en más de 24, cuando se suman, el número permanece igual al mayor entre ellos.
Para una conversión conveniente, cargué un pequeño programa en C.
#include <stdio.h> union IntFloat { unsigned int integerValue; float floatValue; }; void printBits(unsigned int x) { int i; for (i = 31; i >= 0; i--) { if ((x & ((unsigned int)1 << i)) != 0) { printf("1"); } else { printf("0"); } if (i == 31) { printf(" "); } if (i == 23) { printf(" "); } } printf("\n"); } int main() { union IntFloat b0; b0.floatValue = 59.578125; printBits(b0.integerValue); b0.integerValue = 0b01000010011011100101000000000000; printf("%f\n", b0.floatValue); return 0; }
El paso de la cuadrícula es la diferencia mínima entre dos números de punto flotante adyacentes. Si representamos la secuencia de bits de un número como un entero regular, entonces el número de punto flotante vecino diferirá en bits como un entero por unidad.
Se puede expresar de otra manera. Dos números de coma flotante adyacentes diferirán en 2 ^ (E - 127 - 23). Es decir, por una diferencia igual al valor del bit menos significativo.
Como prueba, puede cambiar main en el código y compilar nuevamente.
union IntFloat b0, b1, b2; b0.floatValue = 59.578125F; b1.integerValue = b0.integerValue + 1; b2.floatValue = b1.floatValue - b0.floatValue; printBits(b0.integerValue); printBits(b1.integerValue); printBits(b2.integerValue); printf("%f\n", b0.floatValue); printf("%f\n", b1.floatValue); printf("%f\n", b2.floatValue); short exp1 = 0b10000100; short exp2 =0b01101101; b0.integerValue = 0b01000010011111111111111111111111; b1.integerValue = b0.integerValue + 1; b2.floatValue = b1.floatValue - b0.floatValue; printBits(b0.integerValue); printBits(b1.integerValue); printBits(b2.integerValue); printf("%f\n", b0.floatValue); printf("%f\n", b1.floatValue); printf("%f\n", b2.floatValue); printf("%d %d\n", exp1, exp2);
Creo que por hoy puedes redondear, de lo contrario resulta demasiado largo. La próxima vez escribiré sobre agregar números de punto flotante y perder precisión al redondear.
PD: Entiendo que no toqué el tema de los números desnormalizados, etc. Simplemente no quería cargar mucho el artículo, y esta información se puede encontrar fácilmente en el estándar IEEE 754 casi desde el principio.