• Viernes 8 de Noviembre de 2024, 18:48

Autor Tema:  Atof hace cosas distintas!!! ¿Cual es el problema?  (Leído 3391 veces)

tiwanacote

  • Nuevo Miembro
  • *
  • Mensajes: 11
    • Ver Perfil
Atof hace cosas distintas!!! ¿Cual es el problema?
« en: Jueves 9 de Junio de 2011, 00:48 »
0
Gente, soy nuevo en esto y estoy probando con el comando atof para convertir cadena tipo C a double

El tema que que tengo es que hace cosas distintas según como cargue la cadena (mete basura)

En el siguiente programa aplico atof a tres cadenas distintas: numero1, numero2, y numero3. Finalmente imprime en pantalla los numeros cargados mas otros inesperados. Ademas hago una resta para verificar si aunque sea la variable la convierte bien, y lo logra.

La pregunta es ¿Porque imprime basura? ¿Que hago mal? Lo corri con dos compiladores distintos y ocurre lo mismo

(En este programa se que meto librerias que no van...pero no importa)
Saludos!
Maxi

Código: C++
  1.  
  2.  
  3. #include <fstream>
  4. #include <iostream>
  5. #include <cstdlib>
  6. #include <string>
  7. #include <iomanip>
  8.  
  9.  
  10.  
  11.  
  12. int main()
  13. {
  14.     using namespace std;
  15.  
  16.     cout.setf(ios::fixed);
  17.     cout.setf(ios::showpoint);
  18.  
  19.     double numero_salida = 0;
  20.   
  21. // PRIMER NUMERO
  22.     char numero1[] = "123.45678900000000000000";
  23.     numero_salida = atof(numero1);  // Convierto char a double
  24.  
  25.  
  26.     cout << endl;
  27.     cout << "El numero 1 convertido es:" << endl;
  28.     cout << "--------------------------" << endl;
  29.     cout << setprecision(20) << numero_salida << endl;
  30.     // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada
  31.     cout << "  " <<  setprecision(20) << numero_salida - 123.456789 << endl;;
  32.     cout << endl;
  33.     cout << endl;
  34.  
  35.  
  36.  
  37. // SEGUNDO NUMERO
  38.  
  39.     char numero2[] = "12.45678900000000000000";
  40.     numero_salida = atof(numero2);
  41.  
  42.     cout << endl;
  43.     cout << "El numero 2 convertido es:" << endl;
  44.     cout << "--------------------------" << endl;
  45.     cout << setprecision(20) << numero_salida << endl;
  46.     // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada
  47.     cout << " " <<  setprecision(20) << numero_salida - 12.456789 << endl;;
  48.     cout << endl;
  49.     cout << endl;
  50.  
  51.  
  52.  
  53. // TERCER NUMERO
  54.  
  55.     char numero3[] = "1.45678900000000000000";
  56.     numero_salida = atof(numero3);
  57.  
  58.     cout << endl;
  59.     cout << "El numero 3 convertido es:" << endl;
  60.     cout << "--------------------------" << endl;
  61.     cout << setprecision(20) << numero_salida << endl;
  62.     // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada
  63.     cout << setprecision(20) << numero_salida - 1.456789;
  64.     cout << endl;
  65.     cout << endl;
  66.  
  67.  
  68.     return (0);
  69.  
  70. }
  71.  
  72.  
  73.  
  74.  

ProfesorX

  • Moderador
  • ******
  • Mensajes: 796
  • Nacionalidad: mx
    • Ver Perfil
Re: Atof hace cosas distintas!!! ¿Cual es el problema?
« Respuesta #1 en: Viernes 10 de Junio de 2011, 04:32 »
0
Haz descubierto el error de redondeo de los numeros en punto flotante. Bienvenido al mundo de los numeros aproximados en computadoras ;)

El problema no esta en la funcion atof(), desafortunadamente el problema esta en la manera como se almacenan los numeros de punto flotante internamente. Dado que al guardar un numero en la computadora no podemos guardar una infinita cantidad de digitos (que seria lo ideal, para poder representar cualquier numero), se elige una cierta cantidad de bytes para representar los digitos, con exponente y la mantisa. Pero ocurre que al estar limitados en cuanto a espacio, al hacer la conversion siempre habra algun error de "redondeo" a la hora de almacenar el numero.

En algunos casos y con algunos numeros no habra error de redondeo, pero en otros casos, como bien acabas de descubrir, el error en el redondeo se hara evidente. Entonces lo que ocurre en tu programa, es que cuando conviertes el numero a doble con atof, aparece el error, y despues cuando le restas el mismo numero, al volverlo a convertir internamente a double, vuelve a ocurrir el mismo error de redondeo, entonces los dos numeros se convierten interna y equivocadamente en el mismo y por lo tanto se anulan. Por eso tu resta da cero.

Puedes comprobar tu mismo como se guarda internamente el digito en el depurador examinando el contenido de las variables, o con el siguiente programa puedes verificar que el numero que se guarda en realidad no es el mismo numero exacto que escribiste, debido al redondeo.

Código: C++
  1.  
  2. #include <fstream>
  3. #include <iostream>
  4. #include <cstdlib>
  5. #include <string>
  6. #include <iomanip>
  7.  
  8. int main()
  9. {
  10.     using namespace std;
  11.  
  12.     cout.setf(ios::fixed);
  13.     cout.setf(ios::showpoint);
  14.  
  15.     double numero_salida = 0;
  16.  
  17. // PRIMER NUMERO
  18.     numero_salida = 123.456789;
  19.     cout << "El numero 1 es: " << setprecision(20) << numero_salida << endl;
  20.  
  21. // SEGUNDO NUMERO
  22.     numero_salida = 12.456789;
  23.     cout << "El numero 2 es: " << setprecision(20) << numero_salida << endl;
  24.  
  25. // TERCER NUMERO
  26.     numero_salida = 1.456789;
  27.     cout << "El numero 3 es: " << setprecision(20) << numero_salida << endl;
  28.  
  29.     return (0);
  30.  
  31. }
  32.  
  33.  

Hay que tener este redondeo en cuenta siempre, y es por eso que para programas que requien un alto grado de precision en cuanto a digitos, se ocupan otras alternativas en lugar de usar double. Esa tambien es la razon por la que en C# existe un nuevo tipo llamado Decimal que utiliza 128 bits para almacenar un numero, y que tiene 28-29 digitos de precision, por lo cual es el formato ideal para almacenar numero en los cuales debemos reducir el error de redondeo al minimo, como por ejemplo los calculos financieros, aunque desconozco si en C++ exista un tipo similar a ese.

Saludos :)

NOTA:
==================================================================
Este foro es para ayudar, aprender, compartir... usenlo para eso,
NO SE RESUELVEN DUDAS POR MENSAJE PRIVADO Y MENOS POR CORREO
==================================================================

su -

  • Moderador
  • ******
  • Mensajes: 2349
    • Ver Perfil
Re: Atof hace cosas distintas!!! ¿Cual es el problema?
« Respuesta #2 en: Sábado 11 de Junio de 2011, 05:28 »
0
Aqui esta tu solucion: http://gmplib.org
*******PELIGRO LEE ESTO!!*******

There is no place like 127.0.0.1

Conecto luego existo, no conecto luego insisto.

tiwanacote

  • Nuevo Miembro
  • *
  • Mensajes: 11
    • Ver Perfil
Re: Atof hace cosas distintas!!! ¿Cual es el problema?
« Respuesta #3 en: Miércoles 15 de Junio de 2011, 04:54 »
0
Antes que nada gracias por la atención!

Lo que no entiendo es porque si yo defino la variable "numero" como DOUBLE en donde podría representar hasta 15 dígitos, cuando convierto 7 dígitos con "atof" y uso "setpresicon(20)  para imprimir el resultado, obtengo basura en el dígito 8,9,10... Se puede observar en el tercer numero..

Gracias!

ProfesorX

  • Moderador
  • ******
  • Mensajes: 796
  • Nacionalidad: mx
    • Ver Perfil
Re: Atof hace cosas distintas!!! ¿Cual es el problema?
« Respuesta #4 en: Miércoles 15 de Junio de 2011, 07:49 »
0
Cita de: "tiwanacote"
Lo que no entiendo es porque si yo defino la variable "numero" como DOUBLE en donde podría representar hasta 15 dígitos, cuando convierto 7 dígitos con "atof" y uso "setpresicon(20)  para imprimir el resultado, obtengo basura en el dígito 8,9,10...

Ya te explique el porque, pero vamos de nuevo a aclarar algunos conceptos mas, que quizas te ayude a comprender mejor.

En primer lugar, no es "basura" como la llamas, es como dije antes, un error de redondeo, y esto ocurrira siempre, algunas cantidades pareceran correctas, pero en otras se notara este error de redondeo.

El que puedas representar un numero de 15 digitos, con double, no significa que este numero sera exacto, el formato y el estandar IEEE 754 de los double, no dice nada de que seran 15 digitos significativos sin error. (a diferencia del tipo Decimal que existe en C# como dije antes, que ahi si se garantiza una precision sin error en los digitos significativos de 28). Lee lo siguiente, (en ingles) ahi viene mas explicado el porque:

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

Por ultimo, el compañero su ya te dio una alternativa, si de veras te preocupa la exactitud, utiliza la libreria qe te recomendo.

Saludos :)

NOTA:
==================================================================
Este foro es para ayudar, aprender, compartir... usenlo para eso,
NO SE RESUELVEN DUDAS POR MENSAJE PRIVADO Y MENOS POR CORREO
==================================================================

tiwanacote

  • Nuevo Miembro
  • *
  • Mensajes: 11
    • Ver Perfil
Re: Atof hace cosas distintas!!! ¿Cual es el problema?
« Respuesta #5 en: Jueves 16 de Junio de 2011, 02:00 »
0
Muchas gracias!!!!!
Probare!