[C] e^x, si x es grande, numero negativo

Iniciado por edr89, 3 Junio 2013, 05:26 AM

0 Miembros y 2 Visitantes están viendo este tema.

edr89

Hola,
tengo que calcular el valor de e^x con la serie 1+x+((x^2)/2!)+((x^n)/n!)
encontre un tema relacionado, el código descrito da el mismo resultado que el que yo he hecho.

1- No estoy seguro si he definido bien las variables
2- Para numeros muy grandes el resultado es falso, se arroja un numero negativo, leí en otro tema que se debe definir una funcion que permita numeros muy grandes (a la hora de calcular el factorial) porque la compilacion tiene un limite.

¿Se puede definir una variable mayor a long long?

Como me ha sucedido en ocasiones anteriores, el programa funciona pero no tengo la certeza de que el código este bien escrito. Comentarios y sugerencias bienvenidas.

#include <stdio.h>
#include <math.h>
main()
{
    int e,respuesta,n;
    float resultado=0;
    printf("Calcular e^x\n ");
    printf(" Valor de x: ");
    scanf("%d",&respuesta);
    printf(" Numero de terminos: ");
    scanf("%d",&n);
    for(e=n;e>1;e--)
        {
            resultado+= (pow(respuesta,n)) / (facto(n));
            n--;
        }
    resultado += 1 + respuesta;

    printf("\ne^%d: %.4f",respuesta,resultado);
}

facto(long long int num)
{
   int i=num;
   long long fact=1;
   for(i=num;i>1;i--)
    {
        fact*=i;
    }
    return fact;
}

saludos!

leosansan

#1
Cita de: edr89 en  3 Junio 2013, 05:26 AM
Hola,
tengo que calcular el valor de e^x con la serie 1+x+((x^2)/2!)+((x^n)/n!)

1- No estoy seguro si he definido bien las variables
2- Para numeros muy grandes el resultado es falso, se arroja un numero negativo, leí en otro tema que se debe definir una funcion que permita numeros muy grandes (a la hora de calcular el factorial) porque la compilacion tiene un limite.

¿Se puede definir una variable mayor a long long?


* Aparentemente as tienes bien definidas.

* Es el gran problema de C/C++, como el número entero tenga del orden de 20 cifras cruje, no da para más, sino prueba con 100 y 100 y veras.

* Tendría que repasar las nuevas librerías del estandar C99.

En todo caso el código que utilizas cojea en algunas cosas.

*  Por ejemplo, cuando se trata de calcular e^x el número de términos ha de ser algo secundario ya que lo que en realidad interesa es calcularlo con una determinada precisión, y es esa precisión la que determina automáticamente el número de términos a tomar. En tu caso podrías entrar 3^10 tomando 10 sumandos y no tendrías una referencia de la precisión de ese cálculo.

* Otro detalle es la ineficiencia de la forma de calcular e^x, en el sentido que utilizas pow, que en si mismo no está mal, y sobre todo en la forma de calcular el factorial, culpable en números grandes- el factorial crece muy rápidamente- con la combinación de pow. Ambos juntos hace que a poco que crezcan los números y el número de  términos de la serie a tomar obtengas resultados fuera del alcance o capacidades de C/C++.

* Un mejor método es tener en cuenta que e se calcula, utilizando el desarrollo en serie, de forma que cada nuevo sumando es el anterior multiplicado por un factor :x/n. Con este método alternativo no tienes ni que usar pow ni hacer el cálculo de factoriales previamente.

* Otra cosa a tener en cuenta es introducir una condición de precisión que determine de forma automática el número de sumandos a tomar.

* Y como último comentario a tu código, manejas pocos decimales para hacer cálculos de esta índole.

* También sería interesante comparar la diferencia entre el valor calculado con el valor exacto, entendiendo por éste el valor que de e^x daría la función e^x de la librería de math, que supongo hace uso de un algoritmo parecido al que te comento y paso a darte como variante del tuyo, y como muestra me da estas salidas:

Código (cpp) [Seleccionar]
Teclea el valor de x: 100
Valor estimado: 2.688117141816136e+043
Valor exacto: 2.688117141816136e+043.
Error cometido: -1.525664384353662e+027 .
Numero de terminos utilizados: 205

Teclea el valor de x: 500
Valor estimado: 1.403592217852838e+217
Valor exacto: 1.403592217852838e+217.
Error cometido: -6.745532683619974e+200 .
Numero de terminos utilizados: 716

Teclea el valor de x: 709
Valor estimado: 8.218407461554972e+307
Valor exacto: 8.218407461554972e+307.
Error cometido: -1.900336232223e+291 .
Numero de terminos utilizados: 964


Y el código para ello, que no se diga que no compartimos secretillos:

Código (cpp) [Seleccionar]

/* e^x
*la condición de finalización de las iteraciones: para este ejercicio
utilizamos un bucle while y dos series (con los nombres serie y
serie2  ) cuya diferencia mutua esta en un término,es decir,
serie2 tiene un término más que serie1 . La razón de esta forma de
calcular la serie la encontramos una vez más en la capacidad de almacenamiento de las variables tipo double : la fracción que
calculamos y que añadimos a la serie es cada  vez más pequeña
y, por tanto, la diferencia entre las dos series  también. Llega
un momento en el que las cifras significativas que   puede almacenar
la variable no son apreciables y la fracción que  sumamos no
representa cambio en las variables: en ese momento las  series
toman el mismo valor, se igualan y, por tanto, se termina  la
ejecución del bucle*/

#include <stdio.h>
#include <math.h>
int main(void)
{
    double x, serie1, serie2, frac;
    int i;
    while (1)
    {
        printf("Teclea el valor de x: ");
        scanf("%lf", &x);
        serie1=1.0;
        frac=x;
        serie2=serie1+frac;
        i=2;
        while (serie1!=serie2)
           {
                serie1=serie2;
                frac=frac*x/i++;
                serie2=serie1+frac;
           }
       printf("Valor estimado: %1.16g\n", serie2);
       printf("Valor exacto: %1.16g.\n", exp(x));
       printf("Error cometido: %1.16g .\n", exp(x)-serie2);
       printf("Numero de terminos utilizados: %d\n\n", i);
    }

   return 0;
}


* Existe otra variante para que el usuario indique la precisión con que quiere calcular e^x, pero eso te lo dejo como un simple ejercicio a partir del código anterior.

Saluditos!. ... ..

flony

hola sobre el tema de los números grandes, estaba buscando para que sirven los int8_t int16_t etc, y salio un tema interesante que lo conseguí de casualidad, dependiendo del nivel de precisión podrías usar algo llamado "punto fijo"...no se si sirva, pero para leerlo y ver si te sirve...acá una cosa que encontré referido al tema
http://www.indicart.com.ar/seminario-embebidos/Elementos%20de%20C%20Embebido.pdf
si un problema no tiene solucion entonces no es un problema...es algo inevitable

leosansan

Cita de: flony en  3 Junio 2013, 14:38 PM
hola sobre el tema de los números grandes, estaba buscando para que sirven los int8_t int16_t etc, y salio un tema interesante que lo conseguí de casualidad, dependiendo del nivel de precisión podrías usar algo llamado "punto fijo"...no se si sirva, pero para leerlo y ver si te sirve...acá una cosa que encontré referido al tema


Por lo que tengo entendido, no logras más allá de los veinte dígito, en enteros, de los que obtendrías con un long long int. Otra cosa es que las nuevas librerías del C99 permiten un mayor control en la declaración del tipo de enteros en cuanto a su precisión/número de dígitos.

Saluditos!. .... ..

bemone

Perdon pero no lei mucho, leete el concepto de Sobrecarga de Funciones para poder utilizar el tipo acorde a tu situacion.
Odio los tipos de variable de Windows.

leosansan

Cita de: bemone en  3 Junio 2013, 15:52 PM
Perdon pero no lei mucho, leete el concepto de Sobrecarga de Funciones para poder utilizar el tipo acorde a tu situacion.

¿Sobrecarga de Funciones en C? . Creo que debes leerte mejor los post anteriores.


Saluditos! ... ..

edr89

Cita de: flony en  3 Junio 2013, 14:38 PM
hola sobre el tema de los números grandes, estaba buscando para que sirven los int8_t int16_t etc, y salio un tema interesante que lo conseguí de casualidad, dependiendo del nivel de precisión podrías usar algo llamado "punto fijo"...no se si sirva, pero para leerlo y ver si te sirve...acá una cosa que encontré referido al tema
http://www.indicart.com.ar/seminario-embebidos/Elementos%20de%20C%20Embebido.pdf

Gracias, lo reviso con tiempo.

Cita de: leosansan en  3 Junio 2013, 07:22 AM
Código (cpp) [Seleccionar]
Teclea el valor de x: 100
Valor estimado: 2.688117141816136e+043
Valor exacto: 2.688117141816136e+043.
Error cometido: -1.525664384353662e+027 .
Numero de terminos utilizados: 205

Teclea el valor de x: 500
Valor estimado: 1.403592217852838e+217
Valor exacto: 1.403592217852838e+217.
Error cometido: -6.745532683619974e+200 .
Numero de terminos utilizados: 716

Teclea el valor de x: 709
Valor estimado: 8.218407461554972e+307
Valor exacto: 8.218407461554972e+307.
Error cometido: -1.900336232223e+291 .
Numero de terminos utilizados: 964


Perdon pero me cuesta trabajo entender el código, tengo que resolverlo con la formula 1+x+(x^n)/n!... por eso solo traduje a C usando la funcion pow y factorial.

while (serie1!=serie2)
           {
                serie1=serie2;
                frac=frac*x/i++;
                serie2=serie1+frac;



La solucion como tal no la puedo dar usando la funcion exp, forzosamente tengo que usar la formula con factorial  :(  (aunque aqui esta solo como referencia)

Me gusto mucho este otro código, lo reviso con tiempo. Yo estaba probando a hacerlo por partes para aprender a definir varias funciones y usarlas en main.

gracias y saludos!


leosansan

#7
Cita de: edr89 en  4 Junio 2013, 05:05 AM
Gracias, lo reviso con tiempo.

Perdon pero me cuesta trabajo entender el código, tengo que resolverlo con la formula 1+x+(x^n)/n!... por eso solo traduje a C usando la funcion pow y factorial.

while (serie1!=serie2)
          {
               serie1=serie2;
               frac=frac*x/i++;
               serie2=serie1+frac;



La solucion como tal no la puedo dar usando la funcion exp, forzosamente tengo que usar la formula con factorial  :(  (aunque aqui esta solo como referencia)

Me gusto mucho este otro código, lo reviso con tiempo. Yo estaba probando a hacerlo por partes para aprender a definir varias funciones y usarlas en main.


La serie es: 1+1x/1 + x^2/ 1*2 + x^3/ 1*2*3 + x^4/1*2*3*4 + .......

Si observas cada nuevo sumando se obtiene del anterior multiplicando por una nueva x y dividiendo por un nuevo numero, que es justo el siguiente del último que se uso. Así la variable frac*x/i+1  irá dando:

i=0        frac=0
i=1        frac=x/1
i=2        frac=x/1 * x/2 = x^2/ 1*2
i=2        frac=x/1 * x/2 * x/3= x^3/ 1*2*3


Y así sucesivamente y la suma de frac dará el desarrollo en serio, Así de simple.

Como aplicación de lo anterior y sin usar exp sino tomando, como tu  habías propuesto, el número de términos:

Código (cpp) [Seleccionar]
Teclea el valor de x: 1
Numero de terminos: 3
Valor estimado: 2.666666666666667


Teclea el valor de x: 10
Numero de terminos: 10
Valor estimado: 12842.30511463845


Teclea el valor de x: 1000
Numero de terminos: 100
Valor estimado: 1.190420382778621e+142


Código (cpp) [Seleccionar]

#include <stdio.h>
#include <math.h>
int main(void)
{
    double x, serie=0, frac;
    int i, n;
    while (1)
    {
       serie=1;
       printf("Teclea el valor de x: ");
       scanf("%lf", &x);
       printf(" Numero de terminos: ");
       scanf("%d",&n);
       frac=1;
       for(i=0;i<n;i++)
       {
           frac=frac*x/(i+1);
           serie+=frac;
       }
       printf("\nValor estimado: %1.16g\n\n\n", serie);
    }
   return 0;
}


Por lo que respecta al uso de funciones yo soy partidario de usarlas en casos de que se repita una parte del código, por ejemplo si estas calculando números combinatorios  hay  teóricamente que calcular tres factoriales, por lo que en este caso el uso de una función factorial nos evita repetir el código para calcular el mismo tres veces. O bien cuando entre varios se esté haciendo un código y queramos repartir la tarea, cada uno hará su parte como una función. Pero en estos casos tan sencillos me parece más oportuno estrujarse el cerebro buscando formas simples y más eficientes, como habrás visto con el uso de la variable frac.

Saluditos! .... ..

edr89

Gracias!, ya lo entendi, la clave es:

fact*=(x/n++);

y como dices me evito lo demas

Saludos!

leosansan

Cita de: edr89 en  7 Junio 2013, 01:49 AM
Gracias!, ya lo entendi, la clave es:

fact*=(x/n++);

y como dices me evito lo demas


Más exactamente:

Código (cpp) [Seleccionar]
frac=frac*x/(i+1);

No confundir con:

Código (cpp) [Seleccionar]
frac=frac*x/(i++);

ya que en este caso sólo se tomarían los términos pares de la serie, ya que en cada ciclo del for harían dos incrementos, el del for y el de la instrucción última.

Saluditos desde Gran Canaria! .... ..