Programma per il calcolo della funzione esponenziale
Salve a tutti, sto cercando di impostare un programma in linguaggio c che permetta di calcolare la funzione esponenziale approssimando il risultato a p cifre decimali, ovvero con una condizione di arresto non naturale che richieda in imput una tolleranza.
Il programma che ho scritto è il seguente:
#include
#include
float epsfunz (void );
float rminfunz(void );
main()
{float x, u, add, sum, sumold, tol, rmin, epsS, espo;
int n;
tol=0.;
sum=0.;
add=1.;
n=0;
u=epsfunz();
rmin=rminfunz();
printf("Inserisci la tolleranza voluta: ");
scanf("%e",&tol);
printf("Inserisci l'esponente: ");
scanf("%e",&x);
if (fabs(sum)>rmin/u)
epsS=u*fabs(sum);
else epsS=rmin;
if(x>=0.)
{
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs((sum-sumold)/sumold)>=(tol+epsS));
espo=sum;
}
else
{x=-x;
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs(sum-sumold)>=(tol+epsS)*fabs(sumold));
espo=1./sum;
}
printf("Il valore dell'e^%e approssimato a %e cifre decimali è %e\n",x,tol,espo);}
Nella fase di compilzione non mi da problemi, ma quando vado ad eseguirlo se qualsiasi valore do' alla tolleranza, mi da come valore di espo sempre un valore intero (es. per tol=4 e per x=1 ottengo espo=2.000000e+00).. Quindi ho certamente commesso qualche errore nell'algoritmo, ma pur avendo inserito alcune stampe all'interno, non riesco proprio a capire cosa possa aver combinato. Qualcuno può gentilmente aiutarmi? Grazie anticipatamente a tutti voi!!!
Il programma che ho scritto è il seguente:
#include
#include
float epsfunz (void );
float rminfunz(void );
main()
{float x, u, add, sum, sumold, tol, rmin, epsS, espo;
int n;
tol=0.;
sum=0.;
add=1.;
n=0;
u=epsfunz();
rmin=rminfunz();
printf("Inserisci la tolleranza voluta: ");
scanf("%e",&tol);
printf("Inserisci l'esponente: ");
scanf("%e",&x);
if (fabs(sum)>rmin/u)
epsS=u*fabs(sum);
else epsS=rmin;
if(x>=0.)
{
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs((sum-sumold)/sumold)>=(tol+epsS));
espo=sum;
}
else
{x=-x;
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs(sum-sumold)>=(tol+epsS)*fabs(sumold));
espo=1./sum;
}
printf("Il valore dell'e^%e approssimato a %e cifre decimali è %e\n",x,tol,espo);}
Nella fase di compilzione non mi da problemi, ma quando vado ad eseguirlo se qualsiasi valore do' alla tolleranza, mi da come valore di espo sempre un valore intero (es. per tol=4 e per x=1 ottengo espo=2.000000e+00).. Quindi ho certamente commesso qualche errore nell'algoritmo, ma pur avendo inserito alcune stampe all'interno, non riesco proprio a capire cosa possa aver combinato. Qualcuno può gentilmente aiutarmi? Grazie anticipatamente a tutti voi!!!
Risposte
- Che cos'è epsfunz?
- Che cos'è rminfunz?
(senza avere queste funzioni non è possibile testare il tuo programma).
- 'sum' è inizializzata a zero, quindi a che serve il test 'if (fabs(sum)>rmin/u)' ? Ne conosci già il risultato.
- Nel ciclo
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs((sum-sumold)/sumold)>=(tol+epsS));
la prima volta, dal momento che sum è inizializzata a zero, hai una divisione per zero.
- Per dividere un float per un intero non c'è bisogno di castare l'intero a float.
- Perché usi float e non double?
- Sei al corrente del fatto che Taylor non è il modo migliore per valutare numericamente le funzioni?
- Che cos'è rminfunz?
(senza avere queste funzioni non è possibile testare il tuo programma).
- 'sum' è inizializzata a zero, quindi a che serve il test 'if (fabs(sum)>rmin/u)' ? Ne conosci già il risultato.
- Nel ciclo
do { sumold=sum;
sum=sum+add;
n=n+1;
add=add*(x/(float)n);
}
while(fabs((sum-sumold)/sumold)>=(tol+epsS));
la prima volta, dal momento che sum è inizializzata a zero, hai una divisione per zero.
- Per dividere un float per un intero non c'è bisogno di castare l'intero a float.
- Perché usi float e non double?
- Sei al corrente del fatto che Taylor non è il modo migliore per valutare numericamente le funzioni?
Ciao, grazie per la tua attenzione, la tua risposta mi fa ragionare su problemi che non avevo considerato affatto. Innanzitutto hai ragione ho dimenticato di scrivere le funzioni, eccole:
float epsfunz (void)
{float eps, E, E2;
E=1.;
do {eps=E;
E=E/2;
E2=E+1;}
while (E2>1.); E=1.;
do {eps=E;
E=E/2;
E2=E+1;}
while (E2>1.);
return eps;}
float rminfunz (void )
{float rmin, r;
r=1.;
while(r!=0) {rmin=r;
r=r/2;};
return rmin; }
come giustamente dici if (fabs(sum)>rmin/u) non serve in quel punto, dovrei metterlo quando il valore di sum assume un altro valore, ma dovrei metterlo quindi all'interno del ciclo do while?
andando avanti, ho fatto proprio una stupidaggine con while(fabs((sum-sumold)/sumold)>=(tol+epsS))... ma per ovviare al problema potrei scrivere while(fabs(sum-sumold)>=(tol+epsS)*(fabs(sumold)) ?
Per quanto riguarda l'uso del cast devo dire che non avendo una buona base di informatica e di linguaggio di programmazione (l'anno scorso ho seguito un corso molto arronzato), ho letto su una guida di linguaggio c che ce n'era bisogno; mentre il tipo double l'ho sentito solo nominare, e sinceramente non conosco la differenza tra double e float, per questo a volte li uso indistintamente.
E infine no, non ne sono al corrente, ma seguendo il corso di calcolo numerico 1, la prof ci ha assegnato di scrivere questo programma proprio con l'uso di Taylor, quindi almeno per il momento devo cercare di attenermi a ciò.
Grazie ancora!
float epsfunz (void)
{float eps, E, E2;
E=1.;
do {eps=E;
E=E/2;
E2=E+1;}
while (E2>1.); E=1.;
do {eps=E;
E=E/2;
E2=E+1;}
while (E2>1.);
return eps;}
float rminfunz (void )
{float rmin, r;
r=1.;
while(r!=0) {rmin=r;
r=r/2;};
return rmin; }
come giustamente dici if (fabs(sum)>rmin/u) non serve in quel punto, dovrei metterlo quando il valore di sum assume un altro valore, ma dovrei metterlo quindi all'interno del ciclo do while?
andando avanti, ho fatto proprio una stupidaggine con while(fabs((sum-sumold)/sumold)>=(tol+epsS))... ma per ovviare al problema potrei scrivere while(fabs(sum-sumold)>=(tol+epsS)*(fabs(sumold)) ?
Per quanto riguarda l'uso del cast devo dire che non avendo una buona base di informatica e di linguaggio di programmazione (l'anno scorso ho seguito un corso molto arronzato), ho letto su una guida di linguaggio c che ce n'era bisogno; mentre il tipo double l'ho sentito solo nominare, e sinceramente non conosco la differenza tra double e float, per questo a volte li uso indistintamente.
E infine no, non ne sono al corrente, ma seguendo il corso di calcolo numerico 1, la prof ci ha assegnato di scrivere questo programma proprio con l'uso di Taylor, quindi almeno per il momento devo cercare di attenermi a ciò.

#include<stdio.h> #include<math.h> float epsfunz (void); float rminfunz (void); int main () { float x, u, add, sum, sumold, tol, rmin, epsS, espo, rminoveru; int n; tol = 0.; sum = 0.; add = 1.; n = 0; u = epsfunz (); printf ("Epsilon macchina è %e\n", u); rmin = rminfunz (); printf ("Il più piccolo reale è %e\n", rmin); rminoveru = rmin / u; printf("Inserisci la tolleranza voluta: "); scanf("%e", &tol); printf("Inserisci l'esponente: "); scanf("%e", &x); if (x >= 0.) { do { sumold = sum; sum = sum + add; n = n + 1; add = add * (x / n); if (fabs(sum) > rminoveru) { epsS = u * fabs(sum); } else { epsS = rmin; } } while (fabs(sum - sumold) >= (tol + epsS) * fabs(sumold)); espo = sum; } else { x = -x; do { sumold = sum; sum = sum + add; n = n + 1; add = add * (x / n); if (fabs(sum) > rminoveru) { epsS = u * fabs(sum); } else { epsS = rmin; } } while (fabs(sum - sumold) >= (tol + epsS) * fabs(sumold)); espo = 1. / sum; } printf("Il valore dell'e^%e approssimato a %e cifre decimali è %e\n",x,tol,espo); return 0; } float epsfunz (void) { float eps, E, E2; E = 1.; do { eps = E; E = E / 2; E2 = E + 1; } while (E2 > 1.); return eps; } float rminfunz (void) { float rmin, r; r = 1.; while (r != 0.) { rmin = r; r = r / 2; }; return rmin; }
Il problema essenziale era quella divisione per zero.
Così funziona, anche se alcune cose non mi sono chiare. Ad esempio, non mi sembra che tu possa dire che il risultato è approssimato a 'tol' cifre decimali.
La differenza tra float e double è semplicissima: float usa 32 bit mentre double ne usa 64. Sulle macchine recenti non c'è nessun vantaggio ad usare float. Nel tuo esempio, però, può avere un senso, visto che ti stai proprio occupando di cose come l'epsilon macchina, che sono diverse per i due tipi.
Se la prof ha assegnato un esercizio su Taylor non si discute, bisogna usare Taylor

PS Usare il tag code rende il listato più leggibile.