[C- vari] Enigma approssimazioni
Salve, sono alle prese con un problema da cui non riesco a venirne fuori.
Ci vorrebbe qualcuno esperto con il sistema floating point.
Uso diversi linguaggi tra cui C, Fortran e Matlab.
La questione è la seguente, ho una riga di codice che dipende da una sola variabile, questa variabile è IDENTICA in tutti e tre gli ambienti sopra citati.
L'output purtroppo anche se di pochissimo, è diverso.
Per apprezzare queste differenze sono andato ad osservare le rappresentazioni esadecimali di queste variabili double.
il codice che segue è il medesimo tra tutti gli ambienti (fidatevi), le prime due righe sono quelle originali, le altre sono delle derivazioni, che dovrebbero produrre lo stesso risultato, almeno a livello matematico.
Osservate e giudicate:
(absc1 e absc2 sono dei valori double impostati che ovviamente non cambiano tra i vari casi sotto elencati )
valori in esadecimale di
absc1
3FE0117FD8ACBD94
absc2
3FDBDD4FCDF1D0EB
Risultati di tabsc1 e tabsc2
le differenze riguardano gli ultimi 4 bit della mantissa, anzi gli ultimi due.
Quali sono i valori esatti ?? quelli di C ? o quelli degli altri 2 ambienti ?
Da notare che con il primo valore di absc1 cambiando le formule si riescono ad ottenere due risultati differenti anche in Matlab e Fortran...
Ci vorrebbe qualcuno esperto con il sistema floating point.
Uso diversi linguaggi tra cui C, Fortran e Matlab.
La questione è la seguente, ho una riga di codice che dipende da una sola variabile, questa variabile è IDENTICA in tutti e tre gli ambienti sopra citati.
L'output purtroppo anche se di pochissimo, è diverso.
Per apprezzare queste differenze sono andato ad osservare le rappresentazioni esadecimali di queste variabili double.
il codice che segue è il medesimo tra tutti gli ambienti (fidatevi), le prime due righe sono quelle originali, le altre sono delle derivazioni, che dovrebbero produrre lo stesso risultato, almeno a livello matematico.
Osservate e giudicate:
(absc1 e absc2 sono dei valori double impostati che ovviamente non cambiano tra i vari casi sotto elencati )
// 1 tabsc1 = (1-absc1)/absc1; tabsc2 = (1-absc2)/absc2; // 2 tabsc1 = (0.5/absc1-1+0.5/absc1); tabsc2 = (0.5/absc2-1+0.5/absc2); // 3 tabsc1 = (1.0/absc1-1.0); tabsc2 = (1.0/absc2-1.0); // 4 tabsc1 = ((0.5-absc1)/absc1+0.5/absc1); tabsc2 = ((0.5-absc2)/absc2+0.5/absc2);
valori in esadecimale di
absc1
3FE0117FD8ACBD94
absc2
3FDBDD4FCDF1D0EB
Risultati di tabsc1 e tabsc2
Compilatore C: gcc (Codeblock) tabsc1 tabsc2 3FEFBA4CD894D781 3FF4BFD02B02FEDB 3FEFBA4CD894D781 3FF4BFD02B02FEDB 3FEFBA4CD894D781 3FF4BFD02B02FEDB 3FEFBA4CD894D781 3FF4BFD02B02FEDB Compilatore Intel Visual Fortran : 3FEFBA4CD894D781 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA Matlab : 3FEFBA4CD894D781 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA 3FEFBA4CD894D782 3FF4BFD02B02FEDA
le differenze riguardano gli ultimi 4 bit della mantissa, anzi gli ultimi due.
Quali sono i valori esatti ?? quelli di C ? o quelli degli altri 2 ambienti ?
Da notare che con il primo valore di absc1 cambiando le formule si riescono ad ottenere due risultati differenti anche in Matlab e Fortran...
Risposte
Direi che una differenza così bassa tra i vari risultati è più che accettabile e prevedibile visto che hai eseguito le tue operazioni in virgola mobile. Ricordati che su un computer avrai sempre a che fare con approssimazioni e nel caso delle operazioni in virgola mobile è sufficiente un diverso ordinamento delle operazioni per ottenere risultati differenti.
Fai conto che l'accuratezza di una serie di operazioni in virgola mobile dipendeda molti fattori:
1) gli stati della FPU (la parte del processore che elabora dati in virgola mobile).
questi determinano la modalità di arrotondamento e la precisione delle istruzioni divide e sqrt.
2) quante volte le variabili floating point passano dai registri della FPU alla memoria.
La FPU elabora dati a precisione estesa (ci sono tre formati floating point IEEE 754: precisione singola 32bit, doppia 64bit, estesa 80bit) e i suoi registri sono quindi di 80bit. Questo sia che le variabili nel sorgente siano float o double. L'unica differenza tra float e double è che quando le variabili nei registri tornano in memoria vengono arrotondate alla precisione con cui sono rappresentate in memoria. Quindi a 32bit se dichiarate float o 64bit se dichiarate double. riassumendo possiamo dire che l'abilità del compilatore nel gestire i registri determina anche l'accuratezza del risultato, a meno che le variabili non siano dichiarate di 80bit ("long double" in C) NB: le configurazioni di debug ricaricano spesso dalla memoria le variabili nei registri!
3) come dice apatriarca, l'ordine delle istruzioni può determinare l'accuratezza dei calcoli e la velocità. I compilatori potrebbero avere un opzione per permettergli di riordinare le istruzioni in virgola mobile.
Io proverei a dichiarare le variabili come "long double" e vedere se continuano e venirti fuori risultati diversi...non conosco però il fortran e non so quindi se è possibile. Inoltre accertati che non siano configurazioni di debug.
Comunque è del tutto normale che ci siano queste leggere discordanze.
1) gli stati della FPU (la parte del processore che elabora dati in virgola mobile).
questi determinano la modalità di arrotondamento e la precisione delle istruzioni divide e sqrt.
2) quante volte le variabili floating point passano dai registri della FPU alla memoria.
La FPU elabora dati a precisione estesa (ci sono tre formati floating point IEEE 754: precisione singola 32bit, doppia 64bit, estesa 80bit) e i suoi registri sono quindi di 80bit. Questo sia che le variabili nel sorgente siano float o double. L'unica differenza tra float e double è che quando le variabili nei registri tornano in memoria vengono arrotondate alla precisione con cui sono rappresentate in memoria. Quindi a 32bit se dichiarate float o 64bit se dichiarate double. riassumendo possiamo dire che l'abilità del compilatore nel gestire i registri determina anche l'accuratezza del risultato, a meno che le variabili non siano dichiarate di 80bit ("long double" in C) NB: le configurazioni di debug ricaricano spesso dalla memoria le variabili nei registri!
3) come dice apatriarca, l'ordine delle istruzioni può determinare l'accuratezza dei calcoli e la velocità. I compilatori potrebbero avere un opzione per permettergli di riordinare le istruzioni in virgola mobile.
Io proverei a dichiarare le variabili come "long double" e vedere se continuano e venirti fuori risultati diversi...non conosco però il fortran e non so quindi se è possibile. Inoltre accertati che non siano configurazioni di debug.
Comunque è del tutto normale che ci siano queste leggere discordanze.