You are currently browsing the monthly archive for febrero 2017.

Cuando estudiaba en la universidad había toda una secuencia de asignaturas (Introducción a los Computadores, Estructura de Computadores I, Estructura de Computadores II, Arquitectura de Computadores, Multiprocesadores, Arquitecturas Vectoriales…) en las que nos enseñaban a “diseñar” un ordenador desde cero, llegando a ser tan complejo como podamos imaginar. Empezamos con circuitos combinacionales (puertas lógicas, comparadores, (semi)sumadores, (de)multiplexores, (de)codificadores, ROMs, PAL, PLA, PLDs, etc.) que se caracterizaban porque su salida solo dependía de la entrada en ese momento y que nos permitían construir, entre otras cosas, Unidades de Proceso, de las que forman parte las  Unidades Aritmético Lógicas o las Unidades de Coma Flotante (es en estas donde se ejecutan las instrucciones del lenguaje máquina de nuestro ordenador);  seguíamos con circuitos secuenciales (biestables, contadores síncronos y asíncronos, registros serie/paralelo, RAMs, etc.) cuya salida dependía además del estado en el que se encontraba un sistema cuando le llegaba la entrada (una máquina de estados) y que nos permitía saber hacer, entre otras cosas, la Unidad de Control y las memorias; y de aquí a diseño de procesadores (cableados o microprogramados), buses, memorias cache, lenguajes máquina, procesadores aritméticos, segmentación de procesadores…

Cuando diseñas lenguajes máquina, estos deben contener conjuntos de instrucciones para manejar diferentes tipos de datos, y uno de los tipos indispensables en infinidad de campos son los reales. Por una parte, esos reales van a tener que operarse en procesadores aritméticos, y por otra, los compiladores deben traducir instrucciones de lenguajes de mas alto nivel que contengan reales a instrucciones del LM. Es por toda esta interrelación entre diferentes niveles de abstracción que se hizo necesaria la especificación de un estándar (algo que todo el mundo va a cumplir si quiere continuar en el juego): el estándar IEEE 754.

Está claro que en unas aplicaciones, por ejemplo a nivel de átomos, vamos a necesitar números reales muy pequeños, mientras que en otras, si trabajamos con estrella o con galaxias, lo que necesitaremos son reales extremadamente grandes. Es por este motivo que se adopta una representación en coma flotante (notación científica donde tenemos una mantisa, una base y un exponente) frente a una representación de punto fijo mucho mas intuitiva al principio.

Si solo utilizáramos naturales, con el binario nos apañaríamos, pero en el cuanto que aparecen los enteros, necesitamos representar el signo mas y el signo menos también con ceros y unos. Ésto nos lleva a tener que reservar un bit para el signo, puesto que el número total de bits siempre va a estar determinado de antemano, y a los conceptos de representación explícita y valor representado.

Para concretar ideas, supongamos el caso sencillo de que tenemos tres bits. Los números binarios representados, los valores explícitos del sistema de representación, son del 0 al 7. Sin embargo, si estamos reservando el primer bit para el signo, donde un 0 significará + y un 1 significará -, los valores que representan esos valores explícitos son, en realidad, del +0 al +3 los cuatro primeros binarios (hasta aquí todo normal), pero ahora el valor explícito 4 representa al número entero -0, el valor explícito 5 representa al -1, el 6 se corresponde con el -2 y el 7 con el -3. Esta representación de los enteros se llama de signo y magnitud. El mayor problema que tiene, además de la duplicidad del 0, es que el algoritmo de suma es diferente del de los naturales (si hemos diseñado el sumador completo de 3 bits para naturales y ahora queremos utilizarlo para enteros, este no funciona: (+1) + (-1) tienen valores explícitos 001 y 101, que son los que vamos a meter al sumador, cuya suma da 110. Pero resulta que este valor explicito representa al -2 y no al 0…)

Existen otras representaciones de enteros, en donde lo que va a cambiar es la relación entre el valor explicito y el valor representado, que intentan resolver los diferentes problemas que nos encontramos: duplicidades del cero, algoritmos aritméticos o lógicos diferentes, rangos asimétricos, etc. Las mas conocidas son el complemento a 1 (donde el algoritmo de suma si es igual para naturales y para enteros), el complemento a 2 (donde conseguimos un solo cero a costa de perder la simetría de los rangos) y las representaciones en exceso, bias en inglés (donde los negativos tendrán un 0 delante y los positivos un 1, lo cual nos permitirá comparar enteros en comparadores binarios diseñados para naturales).

Y por fin, con todo esto, ya podemos entender las especificaciones de doble precisión del estándar IEEE 754. El estándar IEEE 754 define como representar números reales en simple y doble precisión. Como siempre se ha dicho, en un ordenador todo se almacena como ceros y unos, y el caso de los reales no podía ser menos. En simple precisión dedicaremos 32 bits (del bit 0 al bit 31) al almacenamiento del real, mientras que en doble precisión lo que tendremos serán 64 bits (bits 0, 1, …, 63). Ni uno mas. Ni uno menos… Y por cuestiones de espacio y no duplicidad, nos centraremos a partir de ahora en la doble precisión.

Para empezar, el bit 63 nos especificará el signo del número real que estamos representando.

A continuación, se reservan 11 bits para el exponente: del bit 62 al bit 52. Este exponente está en representación por exceso. Se consiguen buenas propiedades cuando este exceso es 2^{n-1}-1, que es lo que precisamente hace el estándar, de manera que 2^{11-1}-1 = 1023. Para obtener el valor explícito del valor que queremos representar, se le suma a éste el bias. Restaremos el bias para obtener el valor representado a partir del valor explicito. Con 11 bits, el rango de valores explícitos es del 0 al 2047. Por tanto, al restar el 1023 obtenemos el rango de valores representados que va de -1023 al 1024 (observar que, además, los exponentes negativos son mas pequeño que los positivos, algo bueno para utilizar comparadores binarios para comparar exponentes a la hora de hacer operaciones con números en coma flotante). Sin embargo, como el exponente mas pequeño (00000000000 que representa el -1023) y el mas grande (11111111111 que representa 1024) se reservan para casos especiales (representación del 0,  números denormalizados, mas y menos infinito y los Not a Number (NaN)), el rango de valores validos para los exponentes en el caso de números normalizados es de -1022 a 1023.

Finalmente, los últimos 52 bits, del bit 51 al 0, representan la mantisa. Recordemos que, al disponer de un exponente que varia al desplazar la coma (de ahí lo de coma flotante), la representación de un número, de entrada, no es única. Para conseguir esta unicidad, imponemos lo que se llama normalización, que quiere decir que la coma va a estar a la izquierda o la derecha de la primera cifra significativa. Esto en binario quiere decir que, dado cualquier número, siempre vamos a modificar el exponente de manera que la mantisa quede representada como 1. lo que sea o 0.1 lo que sea. Aunque la normalización a nivel general se puede hacer tanto por la izquierda como por la derecha, solo una de estas es la utilizada por el estándar, y es 1.xxx, con el primer uno a la izquierda de la coma. Además, como siempre vamos a tener este bit a 1, no lo vamos a almacenar de manera explicita y vamos a tener lo que se llama representación con bit oculto (aunque no hemos mencionado nada al respecto, lo mismo pasa con la base, que como ya sabemos que es dos, también queda almacenada de manera implícita). Con todo esto, dada una secuencia de 52 bits

Llegados a este punto, ¿para que sirven los exponentes que nos hemos reservado? Pues para tratar casos especiales. El primero es la representación del 0. Si todo número representado se tiene que interpretar como 1.xxx, esta claro que nunca vamos a conseguir, si no hacemos nada al respecto, una representación explicita del cero. Lo que hacemos es que, cuando todos los bits del exponente están a 0 (exponente -1023) y toda la mantisa también está a 0, entonces este número se interpreta como el 0. Para el mismo exponente pero mantisas diferentes de 0, tenemos los números denormalizados (se van a interpretar con bit oculto a 0, es decir, su valor sera 0.xxx con xxx siendo el valor de la mantisa en lugar de interpretarse como 1.xxx). El matlab, por ejemplo, soporta algo de esto, pues realmin nos da el mínimo numero representable en coma flotante, 2.225073858507201e-308, pero si hacemos 0.0000001/(10^308), obtenemos 9.999999984816838e-316, que es mas pequeño. La explicación es que el primero solo es el mas pequeño de los normalizados, mientras que el segundo es un denormalizado. El mas pequeño posible de los denormalizados nos lo da el comando matlab con eps(0.0). Lo mismo ocurre con el exponente mayor, que es 1024: se utiliza para representar + \infty o -\infty cuando toda la mantisa esta a cero y en función del signo; y para los NaN cuando la mantisa es diferente de 0.

Con todo esto, sea s el bit de signo, e los bits de exponente y m los de mantisa. El valor real es -(1)^s (2^{e-1023})(1.m) si e \in (0,2047). Si e=0 y m \neq 0 entonces el valor es -(1)^s (2^{e-1023})(0.m). Si e=0 y m=0 entonces el valor es 0. Todo lo demás, e = 2047, son casos que no representan números sino casos especiales (infinitos, NaN…). Y fuera de aquí tenemos overflows i underflows.

Y ya para acabar. En general , y a la luz del estándar, para que un sistema en coma flotante en un computador quede determinado, necesitamos especificar, como mínimo, el número total de bits que se utilizarán (uno será el signo), cuantos serán para la mantisa, cuantos se utilizarán para el exponente y cual será el exceso de su representación, como representaremos el cero y cual es el criterio de normalización.

 

Antes que nada, a ver si retomamos este blog, que está abandonadísimo.

No he podido evitar la tentación de poner esta entrada, no sea que esta tarde en nasatv anuncien que han podido estudiar la atmósfera con Spitzer de un exoplaneta potencialmente habitable y haya sorpresas 😉

 

febrero 2017
L M X J V S D
« Nov    
 12345
6789101112
13141516171819
20212223242526
2728