Programación General > C/C++
Atof hace cosas distintas!!! ¿Cual es el problema?
tiwanacote:
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++ --- #include <fstream>#include <iostream>#include <cstdlib>#include <string>#include <iomanip> int main(){ using namespace std; cout.setf(ios::fixed); cout.setf(ios::showpoint); double numero_salida = 0; // PRIMER NUMERO char numero1[] = "123.45678900000000000000"; numero_salida = atof(numero1); // Convierto char a double cout << endl; cout << "El numero 1 convertido es:" << endl; cout << "--------------------------" << endl; cout << setprecision(20) << numero_salida << endl; // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada cout << " " << setprecision(20) << numero_salida - 123.456789 << endl;; cout << endl; cout << endl; // SEGUNDO NUMERO char numero2[] = "12.45678900000000000000"; numero_salida = atof(numero2); cout << endl; cout << "El numero 2 convertido es:" << endl; cout << "--------------------------" << endl; cout << setprecision(20) << numero_salida << endl; // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada cout << " " << setprecision(20) << numero_salida - 12.456789 << endl;; cout << endl; cout << endl; // TERCER NUMERO char numero3[] = "1.45678900000000000000"; numero_salida = atof(numero3); cout << endl; cout << "El numero 3 convertido es:" << endl; cout << "--------------------------" << endl; cout << setprecision(20) << numero_salida << endl; // con lo que sigue se muestra que por mas que aparezca basura la variable esta bien cargada cout << setprecision(20) << numero_salida - 1.456789; cout << endl; cout << endl; return (0); }
ProfesorX:
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++ --- #include <fstream>#include <iostream>#include <cstdlib>#include <string>#include <iomanip> int main(){ using namespace std; cout.setf(ios::fixed); cout.setf(ios::showpoint); double numero_salida = 0; // PRIMER NUMERO numero_salida = 123.456789; cout << "El numero 1 es: " << setprecision(20) << numero_salida << endl; // SEGUNDO NUMERO numero_salida = 12.456789; cout << "El numero 2 es: " << setprecision(20) << numero_salida << endl; // TERCER NUMERO numero_salida = 1.456789; cout << "El numero 3 es: " << setprecision(20) << numero_salida << endl; return (0); }
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 :)
su -:
Aqui esta tu solucion: http://gmplib.org
tiwanacote:
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:
--- 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...
--- Fin de la cita ---
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 :)
Navegación
[#] Página Siguiente
Ir a la versión completa