Regístrate | Conectar
El Tamiz Libros Recursos Series Únete 15 Users Online
Skip to content

Computador mágico XI – Representación binaria III: coma flotante




La serie “El computador mágico” está disponible también en forma de libro.

En el último artículo de la serie nos dedicamos a representar números enteros, tanto positivos como negativos. Hoy vamos a dedicarnos a los números reales… o al menos a una aproximación razonable.

Para ello lo mejor es empezar por la notación científica en base 10. Nos vamos a la Wikipedia y buscamos, por ejemplo, la masa de la Tierra. En el momento de escribir esto, nos dice que la masa de la Tierra es de 5,9736\cdot{}10^{24}. Al primero de los números, el 5,9736, se le llama mantisa, y al 24 se le llama exponente. De hecho, habrás visto que las calculadoras incluso se ahorran el 10 de la potencia y directamente escriben 5,9736E24. La E es de exponente y se asume que, dado que estamos usando números en base decimal, la potencia es 10.

Por cierto, antes de continuar: en realidad la mantisa no se llama mantisa, sino significando. La mantisa es otra cosa. Desgraciadamente, todo el mundo usa “mantisa” para referirse a eso, así que yo haré lo mismo.

 

 

Bueno, pues en base binaria es lo mismo. Imaginemos que tenemos un número con decimales… ¿qué sé yo…? Por ejemplo el 5,5. Si lo ponemos en binario, sería algo como 101,1.

¡Un momento! ¿Cómo hemos hecho eso?

Es sencillo: si cada bit a la izquierda de la coma era una potencia de 2, cada bit a su derecha es una potencia negativa de dos:

101,1=1\cdot{}2^2+0\cdot{}2^1+1\cdot{}2^0+1\cdot{}2^{-1}=4+1+0,5=5,5

A lo mejor no te habías dado cuenta, pero espero que una vez que has visto el ejemplo digas “¡claro, no puede ser de otra forma!”.

Bueno, pues ya tenemos el 5,5 en notación binaria: 101,1.

Ahora tenemos que ponerlo en notación científica binaria. Para ello tenemos que mover la coma hacia la izquierda o la derecha hasta que quede uno y solo un dígito a la izquierda de la coma. Por cada movimiento a la izquierda (como en este caso) aumentamos en 1 la potencia; y por cada movimiento a la derecha, la disminuimos en uno. Piénsalo bien y verás que es exactamente igual que la notación científica en base decimal… con una salvedad, que será importante luego: el primer bit, el que queda a la izquierda de la coma, siempre siempre es un 1. ¿Por qué? Por que si fuera un 0, deberíamos mover la coma un paso hacia la derecha (disminuyendo en 1 el exponente). Esto será importante más adelante.

Así que lo que tenemos es 1,011\cdot{}2^{2}.

Vamos a representar esto ahora con 32 bits. Vamos a dedicar 24 bits para la mantisa y 8 para el exponente. Pero fíjate en que tanto uno como otro pueden ser negativos: si la mantisa es negativa, es porque el número era negativo; y si el exponente es negativo es que el número era menor que 1. Ambos casos son posibles.

La mantisa vamos a codificarla según el método signo-magnitud, usando 24 bits (1 para el signo y 23 para la magnitud). Pero fíjate en que la magnitud, tal y como hemos visto antes, siempre tiene un 1 justo a la izquierda de la coma. Siempre-siempre. Por lo tanto, ese 1 no se almacena… sería desperdiciar un hueco en algo que ya sabemos que es siempre un 1. Fíjate en que para la magnitud no rellenamos con 0s a la izquierda (como hacíamos con los enteros), sino a la derecha. Esto es así porque esos bits representan decimales. Recuerdas que en los números en base 10 podíamos añadir ceros a la derecha de los decimales sin miedo, ¿verdad? Pues aquí, lo mismo. Por lo tanto, nuestra mantisa quedaría: signo=0, magnitud=01100000000000000000000.[1]

El exponente vamos a codificarlo mediante el método exceso-a-127, usando 8 bits. Es decir, nuestro exponente, que es un 2 en decimal, quedaría: 10000001.

Y ahora vamos a colocarlos de un modo un poco extraño:

  • Primero el bit de signo de la mantisa.
  • Luego los 8 bits del exponente en exceso-a-127.
  • Y finalmente los 23 bits de magnitud de la mantisa.

Por lo tanto, nuestro número 5,5 en notación de coma flotante quedaría:

0  10000001  01100000000000000000000

Lo ponemos ahora todo junto:

01000000101100000000000000000000

Vamos a poner algunos ejemplos más, para que los puedas hacer como ejercicio, si quieres:

5,0: 01000000101000000000000000000000

5,125: 01000000101001000000000000000000

-5,125: 11000000101001000000000000000000

5,1: 01000000101000110011001100110011

Uhm… espera un momento… ¿no parece que ese 5,1 es un caso especial? ¿No parece como si todos los demás tuvieran 0s de sobra a la derecha y este en cambio llegara hasta el final? ¡Es periódico! Fíjate en que el periodo 0011 se repite. Pero tenemos un problema, claro. Por muy periódico que sea el número, nosotros solo tenemos 23 bits para almacenar la mantisa, no podemos seguir repitiendo el 0011 hasta el infinito. Bueno… ¿realmente es un problema? Pues sí, sí lo es.

Si ahora intentamos deshacer el cambio, desde esa notación binaria en coma flotante al número decimal… nos saldrá 5,0999999.[2] ¿Qué ha pasado aquí? 5,0999999 no es 5,1. Se parece mucho, pero no es lo mismo.

El problema es que los números reales son infinitos. Pero lo que podemos representar con estos 32 bits es una cantidad finita de ellos. Con mucha precisión, sí. Fíjate en que 5,0999999 se parece mucho-mucho a 5,1. Pero no con precisión infinita. Así que esta notación de coma flotante no puede representar cualquier número con decimales, sino que aproxima al más cercano que puede representar.

Eso es algo que más adelante, cuando hagamos programas que utilicen estas representaciones, debemos tener en cuenta. Por ejemplo, cuando se comparan dos números de coma flotante, no se mira simplemente si son iguales, porque podría ser que las operaciones que se hayan hecho con ellos hayan empeorado ese problema de precisión, y dos números que deberían ser conceptualmente iguales se diferencien en los últimos decimales por culpa de esta representación. Lo que se hace entonces es asumir que dos números de coma flotante son iguales si la diferencia entre ellos es menor que una cierta precisión X. Fíjate en que no tenemos que buscar ni siquiera números muy sofisticados: simplemente un número tan sencillo como el 5,1 es imposible de representar en coma flotante binaria con precisión infinita.

Pero… ¿cómo es posible que no pueda representar el 5,1? Quiero decir que yo entiendo que no pueda representar exactamente el 5,678901234567890123456789. Me faltarían decimales, y eso lo entendería… pero ¿el 5,1? Si te haces esa pregunta es natural, pero es falsa. El 5,1 no tiene nada de sencillo. 5,1 en base decimal es un número muy sencillo, mientras que en base binaria es muy complicado; y en otra bases, vete a saber. No hay nada que diga que la base decimal sea mejor, o más natural, que la base binaria… salvo nuestra propia costumbre. De hecho, más probablemente sea al contrario: si algún día contactamos con una civilización extraterrestre que no comparta nuestra cultura es prácticamente seguro que usaremos alguna forma binaria para comunicarnos, por ser la primera de las bases que se puede usar. Fin de la batallita.

Lo que hemos explicado es lo que se llama “coma flotante de simple precisión” o abreviado a “simple precisión”. Está definido en una norma del IEEE, la 754. Dicha norma define no solamente esta representación de 32 bits, sino también otras de distinta precisión (16 bits, 32 bits, 64 bits, 128 bits…). Prácticamente solo se usan la de 32 bits que ya hemos visto y la de 64 bits (llamada “de doble precisión”)… casi cualquier sistema moderno soporta al menos esas dos.

La norma de doble precisión (64 bits) utiliza 53 bits para la mantisa (uno de ellos para el signo) y 11 para el exponente (en exceso-a-1023), situados de la misma forma: signo-exponente-magnitud.

La aritmética con números de coma flotante no es sencilla. Hacer circuitos eficientes que permitan operar con ellos no es ni medio trivial. Hasta tal punto es así que hasta principios de los años 90 las CPUs de consumo ni siquiera eran capaces de hacerlo. Incluso hasta mediados de los 80 ni siquiera los ordenadores “grandes” las tenían tampoco, salvo como una opción… costosa. No porque los ingenieros de aquella época fueran incapaces de hacerlo, sino porque encarecía tanto el producto que se buscaban alternativas:

  • Si el rendimiento a la hora de hacer esas operaciones no era importante, simplemente se hacía por software. Eso es que, en vez de que lo hiciera la CPU, se hacía un programa que era capaz de hacerlo a base de instrucciones más sencillas (por ejemplo, sabemos que multiplicar 4×5 es sumar 4+4+4+4+4). Esto era tremendamente ineficiente, pero dado que la mayoría de nosotros hacíamos estas operaciones de pascuas a ramos, no importaba.
  • Si el rendimiento sí era importante, se instalaba un hardware especial al lado de la CPU, que hacía esas operaciones y únicamente esas operaciones. Este componente se llamaba coprocesador matemático o coprocesador de coma flotante.

Como decía, hasta principios de los 90 las CPUs que usábamos la mayoría venían sin algoritmia de coma flotante, y se optaba por hacerlo por software. Y solo si para ti era muy importante te comprabas un coprocesador matemático que instalabas al lado de la CPU principal (las placas base de la época tenían un hueco para eso). A partir de esa época los precios bajaron tanto que las CPUs de consumo empezaron a llevar ese coprocesador matemático incorporado en las tripas.[3]

Números de coma fija

Aunque esto es una representación que no existe,[4] vamos a dedicarle unos párrafos a desmitificarlo, porque una vez que te lo cuentan, es trivial. Vamos a contarlo en la base decimal, que probablemente te resulte más fácil, y como ya has visto que la numeración decimal y la binaria son prácticamente lo mismo, podrás aplicarlo tú mentalmente a la representación binaria.

Fíjate en que a la notación científica en base 2 la hemos llamado “de coma flotante”. La coma no está en un sitio fijo, sino que utilizando el exponente podemos llevar la coma más a la izquierda o más a la derecha. Pero podrían ocurrírsenos situaciones en donde nos valga con una precisión fija, y siempre la misma.

Un ejemplo: un sistema contable en euros.[5]

Cuando representamos cantidades en euros, debemos poner dos decimales. Dos, solo dos y siempre dos. Así, tendremos cantidades de 1,23€, -105,50€ o 42,00€. Pero siempre dos. Si hay que rellenar con 0s, se rellena; y si hay que redondear por el final, se redondea. Eso es lo que se llama un sistema de coma fija. ¿Cómo almaceno y represento eso en mi sistema? Si utilizo números enteros no puedo representar los decimales, así que tendría que utilizar 1, 105 y 42 respectivamente (nótese que intencionadamente no pongo el símbolo del €, porque lo de antes era el concepto abstracto y lo de ahora es la representación concreta en el sistema). No solo es que esos 50 céntimos sean mucho o poco dinero, es que la ley me obliga a manejar la contabilidad con dos decimales. La alternativa es representarlos en coma flotante, de modo que tendría 1,23E0, -1,055E2 y 4,2E1 respectivamente. Bueno, más o menos funcionaría, y de hecho muchísimas veces simplemente se hace esto, y a correr. La potencia de nuestros ordenadores hoy en día es tal que el desperdicio de rendimiento por hacer eso es despreciable, así que solo nos queda el problema de la precisión de los números.

Pero hay una alternativa, que es mover la coma dos posiciones a la derecha antes de codificarlo (y lógicamente, moverla dos posiciones hacia la izquierda antes de interpretarla). Así, por ejemplo, utilizaríamos internamente 123, 10550 y 4200 respectivamente. Eso es lo que se llama “coma virtual”.

Aún puedes verlo de otra forma, si quieres: no digas que estás utilizando euros, di que estás usando céntimos. Así, no es que tenga un ingreso de 1,23€, sino un ingreso de 123 céntimos de euro.

O dicho de otro modo: no es que no exista… es que es la misma codificación que ya hemos visto antes para los enteros. Solamente difiere en el lugar en donde ponemos la coma. Si quieres usar coma fija como concepto, usa enteros de una unidad más pequeña. Si, por ejemplo, necesitas manejar distancias en metros con 2 decimales, no uses metros: usa centímetros y una cantidad entera. Si necesitas manejar masas en kilogramos con 6 decimales, no uses kilogramos: usa miligramos y un entero. Y así mil ejemplos.

En el próximo capítulo veremos cómo se representa el texto en base binaria.

 

  1. Recuerda que el primer 1 no se almacena físicamente. []
  2. Si lo vas a hacer con calculadora, cuidado, porque puede que ella tenga el mismo problema. []
  3. Para entendernos: cuando digo “CPUs de consumo” me refiero fundamentalmente a los procesadores Intel que casi todos teníamos en casa. Hasta el modelo 386 no tenían unidad de coma flotante, y se compraban aparte (la unidad de coma flotante diseñada para trabajar con el 386 se llamaba 387; la del 286, 287; había más). Cuando sacaron el 486 lo diseñaron ya con el coprocesador matemático incorporado, pero aún vendieron algunos modelos de 486 con esa unidad desactivada, para atacar al segmento bajo del mercado. []
  4. Luego matizaremos esto. []
  5. Por cierto, ya sé que se el plural oficial de euro es euro. Pero prefiero usar el término que usamos todos en la vida cotidiana, para facilitar la lectura. []

Sobre el autor:

J ( )

 

{ 15 } Comentarios

  1. Gravatar Brigo | 07/01/2013 at 03:14 | Permalink

    Muy bien explicado, así da gusto! :-)

    Yo me intuía que todo sería más o menos así, por los años de práctica, pero siempre está bien saber de donde salen las cosas y porqué. Gracias!

  2. Gravatar antonio | 07/01/2013 at 11:45 | Permalink

    Una explicación muy acertada, la de veces que he tenido que explicarlo y… nunca me han entendido :D

  3. Gravatar Matematicofisico | 09/01/2013 at 04:41 | Permalink

    Gracias J por el artículo, lo esperaba desde hace rato :D

    Hay dos cosas que no tengo claras:

    1. Por ejemplo, el 5.5 (101.1) Si sigo el proceso para pasarlo a punto flotante con 32 bits queda así:

    0 10000001 01100000000000000000000

    Si a mí me entregan ese número tal como está, ¿cómo sé yo cuál es el peso de los 1s de la mantisa?

    Por saber el ejemplo los conozco, pero en el proceso inverso me pierdo un poco =P

    1. J, ¿cómo has hecho para pasar el 5.1 a binario? Ese fue un paso mágico, pero no sé cómo hacer yo para encontrar la periodicidad de ese número. Si me pudieras ilustrar un poco te los agradecería :)

    Saludos J¡

  4. Gravatar J | 09/01/2013 at 03:47 | Permalink

    Matematicofisico:

    Si a mí me entregan ese número tal como está, ¿cómo sé yo cuál es el peso de los 1s de la mantisa?

    0 10000001 01100000000000000000000

    0: es positivo:

    10000001: el exponente es 2

    01100000000000000000000: la mantisa es 1.011

    Es decir, es +1.011×2^2 = 101.1

    ¿Entendido así?

    J, ¿cómo has hecho para pasar el 5.1 a binario?

    El 5 sabes convertirlo, supongo. Es 101.

    Así que lo que te falta es convertir el 0.1. ¿Cómo convertiste el 5 en 101? Vas dividiendo entre dos y vas quedándote con el resto:

    5/2 = 2 resto 1

    2/2 = 1 resto 0

    101

    Pues con los decimales es lo mismo, pero en vez de dividiendo tienes que ir multiplicando (porque las potencias son negativas, no positivas):

    0,1×2=0,2 así que me quedo con el 0 y sigo con el 0,2 (sigue leyendo hasta el final si no entiendes esto)

    0,2×2=0,4 así que me quedo con el 0 y sigo con el 0,4

    0,4×2=0,8 así que me quedo con el 0 y sigo con 0,8

    0,8×2=1,6 así que me quedo con el 1 y sigo con el 0,6

    0,6×2=1,2 así que me quedo con el 1 y sigo con el 0,2

    0,2×2=0,4 así que me quedo con el 0 y sigo con el 0,4

    ¿Sigo? Debería seguir hasta los 23 bits, pero como ya he entrado en el bucle…

    Pongo todos los decimales juntos y tengo ,000110011001100…

    Le pongo delante la parte entera y tengo

    101,000110011001100

    Ahora ya solo queda ponerlo en notación científica:

    1,01000110011001100…x2^2

    Y escribirlo en notación IEE754: 01000000101000110011001100110011

    Estoy seguro de que hay un algoritmo mucho más formal, pero este es el que yo conozco.

  5. Gravatar Argus | 09/01/2013 at 04:34 | Permalink

    @ matematicofisico, no estoy muy puesto en el tema y no tengo la demostracion matematica del algoritmo, pero diria que el paso a binario de un decimal entre 0 y 1 se calcula siguiendo estos pasos:

    multiplicamos el decimal por 2, por 4, por 8, por 16… Si da menor que 1 anotamos 0. Si da mayor que 1, anotamos 1 y seguimos con la parte decimal restante el mismo proceso. Por ejemplo, para el caso del 0.1, tendriamos:

    0.1×2=0 ; 0.1×4=0 ; 0.1×8=0 ; 0.1×16=1.6

    Entonces, de momento tenemos tres ceros y un uno (el 1.6 del final lo anotamos como 1 y seguimos con 0.6 el mismo proceso:

    0.6×2=1.2, o sea, anotamos otro 1 (ya van 00011) y seguimos con el 0.2 el mismo proceso:

    0.2×2=0 ; 0.2×4=0 ; 0.2×8=1.6

    En total tenemos 0.00011001 y seguiriamos el proceso con 0.6, lo que nos vuelve a dar la secuencia 1001 que se repite continuamente.

  6. Gravatar Matematicofisico | 09/01/2013 at 04:54 | Permalink

    @J, @Argus.

    Muchas gracias por sus respuestas, ignorar que los ocho bits a exceso 127 son importantes para ver el número en su totalidad fue un error imperdonable jeje.

    Es que yo veía a la mantisa ignorando que sigo estando en base binaria.

    El algoritmo que me muestran para pasar la parte decimal a binario esta genial y con el enfoque que le ha dado Argus me ha quedado más que claro.

    Saludos y ánimos con esta serie que esta muy buena :D

  7. Gravatar Argus | 10/01/2013 at 01:32 | Permalink

    Ups! perdón, J, cuando escribí mi comentario aún no me había refrescado el ordenador tu respuesta :-)

    ¿Hay alguna razón técnica para representar la notación científica como signo-exponente-mantisa, o es un convenio pactado como se podría haber acordado cualquier otro?

  8. Gravatar Matematicofisico | 10/01/2013 at 04:39 | Permalink

    @Argus, si no me equivoco el orden de agrupación es sólo un acuerdo pactado (corrígeme si me equivoco J)

    Es el pacto de la IEEE754 en base binaria, pues según se también existe un pacto de la IEEE754 en base decimal. Esta se usa, según me contaron, para llevar las cuentas en los bancos por ejemplo, donde no puede haber una periodicidad en las cuentas de los usuarios. Te imaginas si te dijeran (tienes 5.0999999999 € :D )

    Saludos.

  9. Gravatar Matematicofisico | 10/01/2013 at 04:46 | Permalink

    Aunque ese pacto que te menciono no se cómo esta codificado para usarse con bits pues 10 no es una potencia exacta de 2 tal como el sistema octal o hexagesimal. :)

  10. Gravatar J | 10/01/2013 at 04:59 | Permalink

    ¿Hay alguna razón técnica para representar la notación científica como signo-exponente-mantisa, o es un convenio pactado como se podría haber acordado cualquier otro?

    Si hay un motivo no arbitrario, yo no lo conozco.

  11. Gravatar Mortimer | 13/01/2013 at 09:54 | Permalink

    El orden de los bits no es arbitrario, está elegido para que que dos números en coma flotante se puedan comparar (ver si uno es >, = o < respecto al otro) de forma binaria de la misma manera que si fuesen números enteros, simplificando con ello el hardware:

    Primero el bit de signo igual que con los enteros, luego el exponente, que se puede considerar como la parte más significativa, ya que si un número tiene un exponente mayor seguro que es mayor, y luego la mantisa, que sólo habría que tener en cuenta si los exponentes son iguales.

    Es decir, que si construyéramos una hipotética unidad comparadora, que tomara por entrada dos grupos de bits, funcionaría igual con parejas de números enteros o parejas de coma flotante sin necesidad siquiera de informarle de que tipo de datos son la entrada.

  12. Gravatar Anonymous | 18/09/2013 at 02:36 | Permalink

    Una pregunta: dices que como siempre la magnitud tiene un uno a la izquierda de la coma este uno no se almacena, pero, ¿que pasa con el numero 0? El 0 no tiene un uno a la izquierda de la coma, ¿como se representaria?

  13. Gravatar J | 20/09/2013 at 05:23 | Permalink

    ¡Muy buena pregunta!

    Pues con la explicación que he dado, la respuesta es bastante sencilla: el 0 no se puede representar. Se siente. Lo más cercano a 0 que podría representar es 1*2^(-127)… que es muy pequeño, pero no es 0. Se siente. Si el 5.1 no se podía representar, el 0 tampoco… ¿qué te pensabas? ¿Que el 0 era un número especial o qué?

    …pausa escénica…

    Pues mira, sí: lo queramos o no, el 0 es un número especial. Si resulta que la norma IEEE 754 no puede representar el 0, probablemente es una mala norma, ¿no?

    Lo que ocurre es que esto es una explicación académica, simplificada. La norma IEEE 754 es un poco más compleja y, dándose cuenta de que no poder representar el 0 es una putada, dice que hay representaciones especiales para el 0 y también para algunos otros números “especiales”, como por ejemplo el +inf, -inf, NaN (not a number, no-un-número; se utilizar por ejemplo para decir que la operación sqrt(-1) devuelve NaN).

    Por ejemplo, el 0 se representa como… adivina qué: 000000000…0000000.

    Si tienes necesidad de detalles más profundos, puedes verlos simplemente en la Wikipedia (http://es.wikipedia.org/wiki/IEEE_coma_flotante) o buscar la norma IEEE 754.

  14. Gravatar Eduardo | 04/12/2013 at 08:38 | Permalink

    Esta serie me parece magnifica y a la altura de lo que nos tienes acostumbrados. enhorabuena.

  15. Gravatar JM | 06/04/2014 at 11:15 | Permalink

    Me encanta el penúltimo párrafo, es lo que hacen para calcular en las tiendas regentadas por asiáticos, por herencia del ábaco, luego dividen en la calculadora entre 100 y ya está ;-)

Escribe un comentario

Tu dirección de correo no es mostrada. Los campos requeridos están marcados *

Al escribir un comentario aquí nos otorgas el permiso irrevocable de reproducir tus palabras y tu nombre/sitio web como atribución.