[C++] Creazione funzione seno
Ciao ragazzi.
è da tutta una mattinata che sbatto la testa contro un programma che non ne vuole sapere di riuscire giusto
in pratica devo calcolare la funzione seno in base proprio alla definizione di seno $sen x = \sum_{i=0}^N i*(-1)^i* x^(2i+1) /((2i+1)!)$ ,con un certo errore stabilito dall'utente. Per cui ho posto un calcolo dei seni a due a due incrementando ogni volta il valore N dove si fermava il calcolo della sommatoria di modo da aumentarne la precisione, ciclo che finiva quando la differenza tra il valore del primo seno e quello del secondo era minore dell'errore immesso da tastiera.. in pratica però qualunque valore da calcolare immettessi e qualunque errore, mi da sempre come risultato di N finale, che porta alla precisione richiesta, 70 e il valore dei due seni sempre 0.00000!! Ho visto anche su internet ma non riesco proprio a capire dove sia l'errore... anche perché il programma è abbastanza piccolo, quindi non so..... Grazie per le risposte!
sono indeciso se pubblicarlo o no anche perché una volta ho visto che c'è la possibilità di scriverlo in una finestrella che scorre.. se mi dite come si fa... comunque per sì e per no lo scrivo..



#include <iostream> #include <stdlib.h> #include <stdio.h> #include <conio.h> #include <math.h> #include <time.h> #include <ctime> #include <cctype> #include <string.h> using namespace std; int l,i,k,n,d1; double eps ; long double x,n1,ju,d2,s[2]; int main() { printf(" Immetti la variabile di cui vuoi calcolare il seno: "); scanf("%lf",&x); printf(" Immetti l'errore di sensibilita': "); scanf("%lf",&eps); n= 50; do{ for(l=0;l<2;l++) { s[l]= 0.0; for(i=0;i<=n;i++) { d1= -1; for(k=0;k<=i;k++) {d1= d1*(-1);} n1= 1.0; d2= 1.0; for(k=1;k<=((2*i)+1);k++) {n1= n1*k; d2= d2*x;} s[l]= s[l]+ (i*((d1*d2)/n1)); } n= n+ 10; } ju= fabs(s[1]-s[0]); } while(ju>eps); printf("\n\n %d",n); printf("\n %lf",s[1]); printf("\n %lf",s[0]) }
Risposte
Così rapidamente vedo almeno un paio di cose davvero discutibili:
[list=1][*:1hzwjhsf] Non stai creando una funzione seno ma semplicemente calcoli il valore all'interno del main.[/*:m:1hzwjhsf]
[*:1hzwjhsf] Stai mettendo decisamente troppe librerie, alcune delle quali non standard, e mescolando nel chiamarle la notazione C++ con quella C.[/*:m:1hzwjhsf]
[*:1hzwjhsf] Quel codice è in C++ per modo di dire. Non capisco perché usi printf e scanf quando ci sono opzioni migliori (printf tanto vale che lo sostituivi con puts).[/*:m:1hzwjhsf]
[*:1hzwjhsf] Il metodo che usi per calcolare il seno è discutibile, il polinomio di taylor approssima il seno nell'intorno di 0 e la precisione diventa inaccettabile lontano. Quindi dovresti per prima cosa assicurarti di trovarti sufficientemente vicino a 0 e poi usare la formula o usarne direttamente un'altra. Tieni conto che cmath non usa quel metodo, e neanche il processore (il processore possiede una istruzione che calcola il seno). Non confondere ciò che è matematicamente corretto con ciò che è appropriato usare.[/*:m:1hzwjhsf][/list:o:1hzwjhsf]
Detto questo sarebbe appropriato che usassi il tag code:
[list=1][*:1hzwjhsf] Non stai creando una funzione seno ma semplicemente calcoli il valore all'interno del main.[/*:m:1hzwjhsf]
[*:1hzwjhsf] Stai mettendo decisamente troppe librerie, alcune delle quali non standard, e mescolando nel chiamarle la notazione C++ con quella C.[/*:m:1hzwjhsf]
[*:1hzwjhsf] Quel codice è in C++ per modo di dire. Non capisco perché usi printf e scanf quando ci sono opzioni migliori (printf tanto vale che lo sostituivi con puts).[/*:m:1hzwjhsf]
[*:1hzwjhsf] Il metodo che usi per calcolare il seno è discutibile, il polinomio di taylor approssima il seno nell'intorno di 0 e la precisione diventa inaccettabile lontano. Quindi dovresti per prima cosa assicurarti di trovarti sufficientemente vicino a 0 e poi usare la formula o usarne direttamente un'altra. Tieni conto che cmath non usa quel metodo, e neanche il processore (il processore possiede una istruzione che calcola il seno). Non confondere ciò che è matematicamente corretto con ciò che è appropriato usare.[/*:m:1hzwjhsf][/list:o:1hzwjhsf]
Detto questo sarebbe appropriato che usassi il tag code:
[code]qui scrivi il codice[/code]
Beh si ok, però io intendevo funzione matematica
poi tutte queste librerie le metto più che altro per ricordarmele quando mi serviranno in futuro, anche perché so che più ne metto meglio è... Poi comunque conta che sono agli inizi quindi non ho molta esperienza con scritture e notazioni diverse da quelle che uso di solito, e ti dico anche che per scelta personale mi sono trovato meglio con printf e scanf che con cout e cin. Poi si, lo so, è un'approssimazione ma l'esercizio è questo... Ho quella formula e devo trasformarla in un programma che faccia i calcoli. Ti ringrazio per aver risposto ma in tutto questo non mi hai ancora detto cosa secondo te non va proprio nel programma.


Lasciando da parte le questioni stilistiche veniamo alla cose serie.
Hai incluso 8 librerie (di cui una due volte
) quando hai usato solo cstdio e cmath. A te potrebbe sembrare nulla ma stai rallentando moltissimo la compilazione, inutilmente. Inoltre stai usando notazione C e C++ nell'includerle e non è un caso se questo ti ha portato ad include time.h due volte, l'una usando la notazione C e l'alta C++. È inoltre una cattiva pratica considerare conio una libreria “base” quando non è neanche standard ed esiste solo su sistemi operativi windows (tra l'altro quasi deprecata dalla stessa microsoft). Insomma il tuo codice compila solo su compilatori windows anche se è un algoritmo generalissimo.
Detto questo mi sfugge il perché il tuo codice abbia tre for annidati quando è sufficiente un solo for. Quindi direi che dovresti ragionare meglio su quello che richiede l'esercizio. Il punto importante è che \[ (-1)^i\frac{x^{2i+1}}{(2i+1)!} = (-1)\frac{x^2}{(2i+1)(2i)}\cdot (-1)^{i-1}\frac{x^{2i-1}}{(2i-1)!} \] quindi si tratta di aggiornare il nuovo addendo usando il precedente. Minimizzare il numero di operazioni serve anche a ridurre gli errori di arrotondamento.
Infine trovo un po' assurdo includere tutto cmath solo per l'uso di fabs. Per certi versi potrebbe essere più sensato implementarlo. Ma queste sono sottigliezze.
Hai incluso 8 librerie (di cui una due volte

Detto questo mi sfugge il perché il tuo codice abbia tre for annidati quando è sufficiente un solo for. Quindi direi che dovresti ragionare meglio su quello che richiede l'esercizio. Il punto importante è che \[ (-1)^i\frac{x^{2i+1}}{(2i+1)!} = (-1)\frac{x^2}{(2i+1)(2i)}\cdot (-1)^{i-1}\frac{x^{2i-1}}{(2i-1)!} \] quindi si tratta di aggiornare il nuovo addendo usando il precedente. Minimizzare il numero di operazioni serve anche a ridurre gli errori di arrotondamento.
Infine trovo un po' assurdo includere tutto cmath solo per l'uso di fabs. Per certi versi potrebbe essere più sensato implementarlo. Ma queste sono sottigliezze.
Ok, ho fatto tutto quello che mi hai consigliato, anche se non ho capito bene il discorso di cmath.. Il problema persiste ancora e ho fatto ricerche su internet, trovando che non sono l'unico che ha incontrato questo problema.. le ho provate un po' tutte provando anche a cambiare denominazione in long long come era suggerito in qualche sito, o provare a mettere in printf e scanf Lf o llf o anche lld o Ld e ti dico compilava anche se ci mettevo scanf("%llllllllllllld",&x)!! quindi boh, a questo punto non so proprio che fare. Poi un 'altra cosa, magari può inquadrare meglio il problema, se metto n iniziale come 50 mi da sempre come risultato di n finale 70, se metto n iniziale 30 mi da come n finale 50, se metto 0 mi da 20, se metto -20 mi da 0! quindi è un po' anche per i fatti suoi... mi da sempre anche come valore dei due seni 0.0000000 tranne se dichiaro le variabili che ho messo long double come semplicemente double; in tal caso mi da valori finali del seno, sbagliati (tipo -4.765), solo per valori immessi da calcolare bassi, tipo 9, ma se immetto un valore come 65 mi da come risultato nan.. Quindi non so proprio che fare. Grazie del tuo aiuto!
Per quanto riguarda il tuo primo problema quello corretto dovrebbe essere %Lf. Detto questo se usi double oppure usi cout e cin ti togli il problema.
A parte questo direi che calcolare il seno di qualsiasi valore maggiore di, diciamo, \(\pi\) è poco saggio e porta ad errori anche abbastanza grossi. Insomma basta analizzare il resto. E questo senza tenere conto di errori di arrotondamento e altre questioni legati al fatto che usi numeri di precisione finita.
A parte questo direi che calcolare il seno di qualsiasi valore maggiore di, diciamo, \(\pi\) è poco saggio e porta ad errori anche abbastanza grossi. Insomma basta analizzare il resto. E questo senza tenere conto di errori di arrotondamento e altre questioni legati al fatto che usi numeri di precisione finita.
No guarda, non penso proprio che siano problemi di arrotondamento perché anche immettendo il valore 1.5 (approssimativamente $pi$/2) mi ritorna il valore -0.445.. che non c'entra proprio niente... Quindi dev'essere un problema di struttura interna, il fatto è che non riesco ad individuarlo... ho provato anche con cin, ma non cambia niente.. Grazie sempre per l'interessamento

Sono decisamente confuso dal tuo codice. La tua soluzione non ha molto senso e forse non fa neanche quello che credi. Considera infatti che la differenza tra due approssimazioni successive è semplicemente il nuovo addendo. Non hai alcun bisogno insomma di memorizzare e confrontare due approssimazioni successive e tanto meno di ripartire ogni volta a sommare tutti gli addendi dall'inizio. Un discorso simile conta poi anche per il calcolo di fattoriale ed esponente.
Ma il problema più grosso non ha nulla a che vedere con il tuo codice.. La serie è sbagliata. Quella corretta è
\[ \sin(x) = \sum_{i = 0}^{\infty} \frac{-1^i\,x^{2i + 1}}{(2i + 1)!} \]
Ma il problema più grosso non ha nulla a che vedere con il tuo codice.. La serie è sbagliata. Quella corretta è
\[ \sin(x) = \sum_{i = 0}^{\infty} \frac{-1^i\,x^{2i + 1}}{(2i + 1)!} \]
Eh, ma conta che mi sono basato proprio su questa formula! Ma se non sottraessi le due approssimazioni come farei a porre la condizione che l'errore nel risultato finale del seno dev'essere minore di quello immesso scelto dall'utente? non si calcola come scarto tra i due seni?
Per confrontare il tuo codice ho scritto la soluzione. È evidente che sei pregato di non copiarla
, ma di correggere il tuo codice in modo appropriato.

#include <cmath> #include <cfloat> #include <iostream> // Definisco la costante PI const long double M_PI = atan2(0.0L, -1.0L); // Creo due funzioni, la prima serve per assicurarsi che i valori siano sensati, l'altra invece calcola materialmente // il seno senza fare ulteriori controlli long double seno(long double const x, long double const toll = 2 * LDBL_EPSILON); long double __seno(long double const x, long double const toll = 2 * LDBL_EPSILON); int main() { long double const x = 1.7L * M_PI; std::cout << fabs(sin(x) - seno(x)) << std::endl; return 0; } long double seno(long double const x, long double const toll) { // Mi assicuro che la tolleranza non sia eccessiva long double const rtoll = (toll < 2 * LDBL_EPSILON) ? 2 * LDBL_EPSILON : toll; long double rx = x, sgn = 1; // Lavoro nell'intervallo [0, 2 PI) if ((x > 2 * M_PI) || (x < 0) ) { rx = fmodl(x, 2 * M_PI); if (rx < 0) rx += 2 * M_PI; } // Mi riduco all'intervallo [0, PI/2] if (rx > M_PI) { sgn = -1; rx = (rx > 1.5 * M_PI) ? 2 * M_PI - rx : rx - M_PI; } else if (rx > 0.5 * M_PI) { rx = M_PI - rx; } return sgn * __seno(rx, rtoll); } long double __seno(long double const x, long double const toll) { long double res = x; long double add = x; long double sgn = 1.0L; int const LIM = 60; int i = 0; long double const x2 = x*x; do { i += 2; sgn *= -1; add *= x2; add /= ( i*(i + 1) ); res += sgn * add; } while ((i < LIM) && (add > toll)); return res; }
Come ti ho già detto, la differenza (in valore assoluto) tra due approssimazioni successive è uguale al valore assoluto dell'ultimo addendo... Non hai quindi bisogno di calcolarti separatamente due approssimazioni consecutive e calcolarne la differenza, ma solo di arrivare ad una approssimazione, verificare se il nuovo addendo è inferiore all'errore e in caso contrario andare avanti.
Tranquillo vict85 anche perché non ho capito bene alcune parti del codice, comunque forse mi hai dato un'idea su come fare, grazie. Non ti disturberò più su questo argomento, anche perché domani proprio ho l'esame di informatica
Bene, grazie a tutti e due veramente!!

