[ Foro de C# ]

225 - 225 = -1.5666 ???

30-Jul-2013 22:34
Andrés Carlos Sánchez Reyes
9 Respuestas

Muy buenas.
Quisiera saber el motivo por el que esa sencilla cuenta me dio un resultado tan raro.
Los numeros 225 estaban en Double, y fue al convertirlos a Int32 cuando se consiguio hacer la resta, dando como resultado un bonito cero.


05-Aug-2013 12:35
Nacho Cabanes (+31)

Pon el fuente, para que lo veamos. Aun así, todos los números reales, tanto los "float" como los "double", tienen una cierta pérdida de precisión. Lo que ocurre es que no debería ser tanta como dices.

En un "float" deberías tener 7-8 cifras de precisión, de modo que podría ocurrir algo como que 0.00000002 - 0.0000001 = 0.000000128, es decir, podría haber imprecisiones a partir de esas 7-8 cifras correctas. En un double deberías tener 15-16 cifras de precisión, de modo que es muy raro que te falle en la tercera cifra.

Mira, he hecho un programa de ejemplo:

 
class prueba225
{
    public static void Main()
    {
        double n1 = 225.0;
        System.Console.Write("Introduce el número a restar de 225: ");
        double n2 = System.Convert.ToDouble(
            System.Console.ReadLine());
        System.Console.WriteLine(n1 - n2);        
    }
}
 


Verás que resta 225 del número que le introduzcas, y que ambos son "double", como en tu caso. He hecho que te lo pida, para que no sera una resta 225 - 225 prefijada, porque el compilador podría optimizarlo y no calcular la resta en tiempo real, sino antes de compilar.

Si lo pruebas, verás que la resta que se obtiene de 225 - 255 es cero, como era de esperar. Por tanto, en tu programa, el error debe estar en algún otro sitio.


06-Aug-2013 02:04
Andrés Carlos Sánchez Reyes

Pues aqui lo dejo, espero haberlo comentado para que se entienda.

 
using System;
 
class Ruedas_de_papel
{
    static void Main()
    {
      double diametroexterior;
	  double diametrointerior;
	  double diametrointerior2;
	  double radiorueda;
	  double longitudpapel;
	  double diferenciaderadio;
	  double divisionA4;
	  double contadory;
	  double ymenor;
	  double xfeo;
	  double y;
	  double R;
	  double R2;
	  double largo;
	  string pausa;
 
	  // Peticion de datos //
	  Console.WriteLine("Introduce el diametro exterior");
	  diametroexterior = Convert.ToInt32(Console.ReadLine());
	  Console.WriteLine("Introduce el diametro interior");
	  diametrointerior = Convert.ToInt32(Console.ReadLine());
	  longitudpapel = Math.PI * (Math.Pow(diametroexterior,2) - Math.Pow(diametrointerior,2)) / 0.112;
	  Console.WriteLine("Longitud total del papel: {0}", longitudpapel);
	  Console.WriteLine("Introduce el radio de perfil de la rueda");
	  radiorueda = Convert.ToDouble(Console.ReadLine());
 
	  // Calculos Previos//
	  divisionA4 = longitudpapel / 280;
	  Console.WriteLine("divion A4: {0}", divisionA4);
	  divisionA4 = Convert.ToInt32(divisionA4);
	  diferenciaderadio = diametroexterior - diametrointerior;
	  contadory = diferenciaderadio / divisionA4;
	  Console.WriteLine("divion A4: {0}", divisionA4);
	  Console.WriteLine("diferencia de radios : {0}", diferenciaderadio);
	  Console.WriteLine("contador y: {0}", contadory);
	  ymenor = radiorueda - diferenciaderadio;
	  y = ymenor;
	  xfeo = 1;
	  pausa = Console.ReadLine();
 
		for (y = ymenor; xfeo != 0; y = y + contadory)//Bucle para crear xfeo y R en un punto determinado de "largo"//
			{
				R = diametroexterior - radiorueda + y;
				xfeo = Math.Sqrt(Math.Abs((Math.Pow(radiorueda,2)) - (Math.Pow(y,2))));
				R = Math.Pow(R,2);//desde aqui//
				R = Convert.ToInt32(R);
				diametrointerior2 = Math.Pow(diametrointerior,2);
				diametrointerior2 = Convert.ToInt32(diametrointerior2);
				R2 = R - diametrointerior2;//hasta aqui. Esto empezo en una sola formula. Pero acabe descomponiendola para ver el fallo//
				Console.WriteLine(" R{0}diametro interior{1}R2 {2}",R,diametrointerior2,R2);
				largo = (Math.PI * R2) / 0.112;
				Console.WriteLine("x   {0}   largo   {1}  y  {2}  R   {3}",xfeo,largo,y,R);
				pausa = Console.ReadLine();
			}
	  pausa = Console.ReadLine();
	}
}
 



06-Aug-2013 02:13
Andrés Carlos Sánchez Reyes

La formula maldita esta entre los comentarios //desde aqui//   //hasta aqui//.
Repito el fuente. Que el Notepad++ me la ha jugado antes

 
using System;
 
class Ruedas_de_papel
{
    static void Main()
    {
      double diametroexterior;
	  double diametrointerior;
	  double diametrointerior2;
	  double radiorueda;
	  double longitudpapel;
	  double diferenciaderadio;
	  double divisionA4;
	  double contadory;
	  double ymenor;
	  double xfeo;
	  double y;
	  double R;
	  double R2;
	  double largo;
	  string pausa;
 
	  // Peticion de datos //
	  Console.WriteLine("Introduce el diametro exterior");
	  diametroexterior = Convert.ToInt32(Console.ReadLine());
	  Console.WriteLine("Introduce el diametro interior");
	  diametrointerior = Convert.ToInt32(Console.ReadLine());
	  longitudpapel = Math.PI * (Math.Pow(diametroexterior,2) - Math.Pow(diametrointerior,2)) / 0.112;
	  Console.WriteLine("Longitud total del papel: {0}", longitudpapel);
	  Console.WriteLine("Introduce el radio de perfil de la rueda");
	  radiorueda = Convert.ToDouble(Console.ReadLine());
 
	  // Calculos Previos//
	  divisionA4 = longitudpapel / 280;
	  Console.WriteLine("divion A4: {0}", divisionA4);
	  divisionA4 = Convert.ToInt32(divisionA4);
	  diferenciaderadio = diametroexterior - diametrointerior;
	  contadory = diferenciaderadio / divisionA4;
	  Console.WriteLine("divion A4: {0}", divisionA4);
	  Console.WriteLine("diferencia de radios : {0}", diferenciaderadio);
	  Console.WriteLine("contador y: {0}", contadory);
	  ymenor = radiorueda - diferenciaderadio;
	  y = ymenor;
	  xfeo = 1;
	  pausa = Console.ReadLine();
 
		for (y = ymenor; xfeo != 0; y = y + contadory)//Bucle para crear xfeo y R en un punto determinado de "largo"//
			{
				R = diametroexterior - radiorueda + y;
				xfeo = Math.Sqrt(Math.Abs((Math.Pow(radiorueda,2)) - (Math.Pow(y,2))));
				R = Math.Pow(R,2);//desde aqui//
				R = Convert.ToInt32(R);
				diametrointerior2 = Math.Pow(diametrointerior,2);
				diametrointerior2 = Convert.ToInt32(diametrointerior2);
				R2 = R - diametrointerior2;//hasta aqui. Esto empezo en una sola formula. Pero acabe descomponiendola para ver el fallo//
				Console.WriteLine(" R{0}diametro interior{1}R2 {2}",R,diametrointerior2,R2);
				largo = (Math.PI * R2) / 0.112;
				Console.WriteLine("x   {0}   largo   {1}  y  {2}  R   {3}",xfeo,largo,y,R);
				pausa = Console.ReadLine();
			}
	  pausa = Console.ReadLine();
	}
 



06-Aug-2013 02:14
Andrés Carlos Sánchez Reyes

Perdon por la duplicidad.


07-Aug-2013 02:02
Nacho Cabanes (+31)

¿Con qué datos de prueba te da los problemas, para poder localizar qué está fallando? Porque no entiendo la lógica que estás intentando desarrollar. Ese "for" es, cuando menos, desconcertante.

He probado con los valores:
diametro exterior = 30
diametro interior = 15
radio de perfil de la rueda = 1

de modo que 30-15 elevado al cuadrado fuera ese 225 que dices que te da problemas, pero como la lógica no es evidente y no hay comentarios que la expliquen, no tengo claro qué pretendes con ese "for" tan raro.

Ten una cosa en cuenta: un for debería tener una condición con un "mayor que" o un "menor que", no con un "distinto de", especialmente si usas números reales, porque quizá los dos números que comparas nunca lleguen a ser estrictamente iguales. Si quieres que se acerque lo suficiente a un cierto número, deberás dejar un cierto margen, por ejemplo una milésima, que podrás comprobar usando el valor absoluto. Sería algo como

if (Math.Abs(x-y) < 0.001) ...


07-Aug-2013 11:59
Andrés Carlos Sánchez Reyes

Voy a explicar , o intentarlo, explicar la logica del programa.
Imaginemos que un neumatico de coche, a partir de la banda de rodadura, pudieras cortarlo en una larga loncha de caucho, esa "loncha" vendria a tener una forma determinada por su diametro interior, su diametro exterior, el grosor de la loncha y el radio que tiene si se mira de frente, forma de U invertida.
Obviamente los neumaticos reales, no hacen la forma que hace el programa. Pero para hacer maquetas sirve.

Este programa, lo que hace es calcular esa loncha para crear una rueda a partir de papel de 80gr/m2.

Primeramente te pide esos datos y te calcula la longitud total del papel. A partir del grosor de una hoja de papel 0.112 mm

En Calculos previos. Se calcula en cuantas hojas de A4 entraria. 280mm una hoja de papel.
Aqui tengo la duda de como podria redondear hacia abajo divisionA4. Espero pudieras resovermela.

El resto son calculos previos del eje y

El bucle, lo que genera son las coordenadas x e y (de la U invertida para mas señas) en el largo del papel.

Y si, tienes razon, falla el bucle. Tratare de modificarlo.
Para que te haga los calculos mal debes quitar las dos lineas 55 y 57, que son las que cambian a Int32 tanto R como diametrointerior2

Los datos que yo introduzco son
diametro exterior 26
diamtetro interior 15
radio 6,6

Si los introduces veras como R = 225 diametrointerior2 = 225 y R2 no da cero


08-Aug-2013 00:44
Nacho Cabanes (+31)

He probado con esos datos, y sí da "casi cero", con la precisión esperable, como te comentaba.

Miran he añadido unos cuantos WriteLine adicionales para ver los valores de todas las variables y he obtenido esto:

RR 6,6
Y -4,4
Xfeo 4,91934955049954
R sin elevar 15
R cuadrado 225
DI2 225
R2 -5,6843418860808E-14

Es decir, R2 no es que valga -5, sino -5 * 10^14 (mira ese E-14 del final, es notación científica). Es decir, el resultado es -0,00000000000005684 que entra dentro de lo esperable para la precisión de un "doble".

Si no quieres que aparezca con 15 cifras decimales, sino con 2-3, que es lo habitual, puedes usar Math.Round, que te permite redondear a la cantidad de cifras que tú quieras (preferible a Convert.ToInt32 si necesitas alguna cifra decimal):

http://msdn.microsoft.com/es-es/library/zy06z30k.aspx

De hecho, si el programa avanza y ese valor lo multiplicas por PI y lo divides entre 0,112 sale a equivaler a multiplicarlo casi por 30, de modo que obtienes que "largo" vale -1.59E-12, ese decir, -0,00000000000159, que es el margen de error esperable. Hablamos de menos de una billonésima, no de una centésima, como parecía indicar tu pregunta:

R 225 diametro interior 225 R2 -5,6843418860808E-14
x   4,91934955049954   largo   -1,59445417051823E-12  y  -4,4  R   225

Lo dicho: supongo que no habías tenido en cuenta que son números en notación científica (la E del final es importantísima), y que puedes redondear a "n" cifras para garantizar que vas a truncar el número respetando las cifras que sabes que son exactas, pero no más.


08-Aug-2013 02:23
Andrés Carlos Sánchez Reyes

Pues si efectivamente ya no me acordaba de la notación cientifica, tantos años sin ir a clase es lo que tiene.
Aun asi me sigue chocando que la resta de dos numeros sin un solo decimal de como resultado decimales.
Ahora a mejorar el programa me toca.
Muchas gracias.


08-Aug-2013 14:01
Nacho Cabanes (+31)

Entiendo el desconcierto que provoca, pero piensa que lo habitual es que internamente los números se almacenen como potencias de 2, de modo que números reales que sean "cortos" de expresar como potencia negativa de 10, pueden no ser tan cortos (o incluso una secuencia infinita) si se expresan como potencia negativa de 2. Por eso siempre es importante pensar cuántas cifras significativas obtienes con un número real, y redondear a ese número de cifras (o menos).

Por ejemplo, 0,5 generaría la secuencia binaria 00111111000000000000000000000000, que se vuelve a convertir a decimal como 0,5.

En cambio, 0,2 se guarda como la secuencia 00111110010011001100110011001101, que se vuelve a convertir como 0.20000000298023224

Hay alguna página web que te permite hacer pruebas online para ver qué ocurre al almacenar números reales. Mira esta, por ejemplo:

http://www.h-schmidt.net/FloatConverter/






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)