[C con Clase] problemas obteniendo la parte decimal de un numero double

Steven Davidson steven en conclase.net
Mie Mar 14 05:38:06 CET 2007


Hola Pedro,

El pasado 2007-03-14 01:27:01, Pedro Mateo escribió:

PM> soy nuevo en c++ y vengo de programar en visual foxpro por mas de 10 anos y no existe otra actividad (NINGUNA!!) que me guste por lo menos un poquito de lo que me gusta programar

Ante todo, bienvenido a nuestra lista y al "mundo" de C++.

PM> ahora tengo un OBSESION de programar en c++ y no me puedo librar de eso, es un reto para mi
PM> quiero obtener la parte decimal de un double pero al intentarlo me han ocurrido algunas cosas que me despiertan varias interrogantes
PM> este programita deberia inprimir 234 en cambio se queda en un loop infinito
PM> a menos que cambies el 23.234 por 23.2 que en tal caso me devolve 2
PM> puedo entender que en el comportamiento de este programa sea diferente cuando los numeros son grandes, pero los numeros que estoy utilizando para probar no son la gran cosa
PM> por otro lado esta el hecho de que los calculos que hasta ahora he realizado solo me devuelven 6 decimales lo que es inapropiado para determinadas ocaciones en las que se necesita mas presicion

Ten cuidado con las precisiones. No confundas la precisión interna de los valores de coma flotante con la precisión mostrada por 'cout <<' el cual realiza una interpretación del valor numérico para representarlo como una cadena de caracteres. En esta conversión, 'cout <<' sólo muestra ciertos caracteres, que representan dígitos, pero no todos. En este caso, si existe una parte decimal, entonces es redondeado para caber en la precisión establecida.

Bajo las STL, puedes usar la función miembro 'precision()'. Por ejemplo,

cout.precision( 10 );
cout << 23.234 << endl;

También puedes indicar el formato de mostrar valores de coma flotante. Por ejemplo,

cout.setf( ios::floatfield, ios::scientific );
cout.precision( 16 );
cout << 23.234 << endl;

Aparecerá en la pantalla:

2.3234000000000002e+001

Por el otro lado, también puedes indicar una notación o formato "fijo". Esto es,

cout.setf( ios::floatfield, ios::fixed );
cout.precision( 16 );
cout << 23.234 << endl;

Aparecería en la pantalla:

23.2340000000000020

PM> se que existen funciones que me pueden ayudar a resolver ese problema, pero estoy en c++ porque deseo aprender como funcionan las cosas, en otros lenguajes de muchisima menor potencia puedo hacer operaciones como esta sin necesidad de utilizar funciones
PM> y se que el dominio de la solucion a este tipo de problemas es vital para mi desarrollo en c++

Veamos el código fuente.

PM> #include <iostream>
PM> using namespace std;
PM> main(){
PM> 	double x=23.234;
PM> 	int c=int(x);
PM> 	
PM> 	while(x-int(x)) {x*=10;c*=10;}
PM> 	cout<<x-c<<endl;	
PM> }
PM> en este programa x-int(x) en todos los casos deberia llegar un punto en que sea igual a 0 pero tal no es el caso, si cambias el valor de x obtendras resultados solo si el numero tiene un decimal de menos de 3 posiciones

Me temo que esto casi nunca da exactamente 0, porque la representación interna no es exacta; casi siempre hay algún bit activado que impide que esto sea cierto. El razonamiento está en que se tiene que convertir un valor representado en el sistema decimal a su representación en el sistema binario. Al tratarse de números fraccionarios, la precisión sufre debida a la inexactitud representativa de tal parte fraccionaria.

La típica solución es someter el valor de interés a una prueba según un nivel de precisión. Por ejemplo,

const double PRECISION = 1000;

double num = 12.345;

if( int( num*PRECISION )/PRECISION == 0 )
{
  // num es 0
}
else
{
  // num no es 0, o al menos no se considera 0
}

Obviamente, para precisión, sería mejor usar números enteros, pero claro está, en general, no podemos representar todos los números enteros, al menos que nuestro problema tenga ciertas restricciones.


Espero haber aclarado la duda.

Steven


Más información sobre la lista de distribución Cconclase