Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Otros temas > La Taberna
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 07-01-2013
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.040
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
El "problema" de los números reales (coma flotante)

Son incontables las ocasiones en la que alguien tiene el "problema" de los decimales que no guarda la base de datos. Son mensajes del tipo: "error grave en firebird, no guarda los decimales", "problema con decimales en mysql", "delphi no sabe redondear", etc.
Sin embargo, "todos" sabemos el motivo, pero por si acaso alguien no lo sabe o, simplemente, quiere entender de una vez por todas cómo funciona, aquí pego una explicación que he encontrado hoy en una web que visito a menudo.
Este es el enlace, esta es la explicación (más claro, imposible):

Cita:
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 . 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:

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 .
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.
Responder Con Cita
  #2  
Antiguo 09-01-2013
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Na... yo me quedo con la explicación del Artículo cuyo nombre le calza justo: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #3  
Antiguo 09-01-2013
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.040
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Cita:
Empezado por Delphius Ver Mensaje
Na... yo me quedo con la explicación del Artículo cuyo nombre le calza justo: What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Pero eso es para los que dominen el inglaterrino
Responder Con Cita
  #4  
Antiguo 09-01-2013
Avatar de Ñuño Martínez
Ñuño Martínez Ñuño Martínez is offline
Moderador
 
Registrado: jul 2006
Ubicación: Ciudad Catedral, Españistán
Posts: 6.000
Poder: 25
Ñuño Martínez Tiene un aura espectacularÑuño Martínez Tiene un aura espectacular
Que conste que el punto fijo tampoco es la panacea. Ya conté el problema que tuve con "medio céntimo". Eso sí, el ejemplo sirve para demostrar por qué hay que saber cómo funcionan las cosas, y por qué un Teleco no debería meterse en el traje de trabajo de un informático...
__________________
Proyectos actuales --> Allegro 5 Pascal ¡y Delphi!|MinGRo Game Engine
Responder Con Cita
  #5  
Antiguo 09-01-2013
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Cita:
Empezado por Casimiro Notevi Ver Mensaje
Pero eso es para los que dominen el inglaterrino
Tu sabes que odio a esa cosa llamada inglés que escriben de atrás pa' adelante
He perdido muchas neuronas al traducirme semejante documento. Si... lo tengo traducido, entero. A mi sacha espanish y si, con ayuda del traductor de Google y otro Ofline.
Pero tengo que reconocer que a pesar de odiarlo, lamentablemente todavía sigue siendo el idioma internacional y donde hay más material, y confiable.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
validar decimales con "coma" lucasarts_18 PHP 3 26-12-2008 01:41:50
Necesito llamar a métodos de clases "hija" desde su clase "padre" Flecha OOP 17 20-04-2007 00:03:53
Reemplazar "," por ":" en un punto flotante DTAR SQL 2 09-05-2006 23:55:07
Añadir signo "+" a una variable flotante Ricsato Varios 2 18-06-2005 00:53:03
convertir la cadena "1.8376e+01" a numero flotante franciscobucio Varios 0 22-10-2004 22:49:47


La franja horaria es GMT +2. Ahora son las 08:25:57.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi