Newsgroup: it.comp.lang.c
Titolo: Problema con numeri reali.

Ciao.
Un esercizio mi chiede di verificare, mediante un semplice programma, 
che la formula sen^2(x)+cos?2(x)=1 sia vera per ogni x reale.
Nel programma, per stampare questa formula, ho usato la specifica di 
conversione %.15e ed ho ottenuto, per alcuni valori di x, il valore 
9.999999999999999e-01, che quasi 1.0, ma non lo raggiunge. 
Al contrario, utilizzando in printf la specifica di conversione %15f, 
ottengo 1.000000000000000 per tutti i valori di x. 
Qualcuno saprebbe spiegarmi perchè sussiste questa differenza?
Grazie e a presto.
Bizio.

 Qualcuno saprebbe spiegarmi perchŠ sussiste questa differenza?
cerca "ieee floating point" su google


%.15f (o %.15e) imposta la "precisione" con cui un numero in virgola 
mobile (eg. un 'double') viene visualizzato nella printf.
Prova tu stesso:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <stdio.h>
int main(void)
{
     int nPrecision;
     double d = 9.999999999999999e-01;
     for(nPrecision = 0; nPrecision <= 30; nPrecision++)
     {   printf("Precisione con \"%%.%df\": %.*f | con \"%%.%de\": %.*e\n",
             nPrecision, nPrecision, d,
             nPrecision, nPrecision, d
             );
     }
     return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== output di esempio: =========
Precisione con "%.0f": 1 | con "%.0e": 1e-00
Precisione con "%.1f": 1.0 | con "%.1e": 1.0e-00
Precisione con "%.2f": 1.00 | con "%.2e": 1.00e-00
Precisione con "%.3f": 1.000 | con "%.3e": 1.000e-00
Precisione con "%.4f": 1.0000 | con "%.4e": 1.0000e-00
Precisione con "%.5f": 1.00000 | con "%.5e": 1.00000e-00
Precisione con "%.6f": 1.000000 | con "%.6e": 1.000000e-00
Precisione con "%.7f": 1.0000000 | con "%.7e": 1.0000000e-00
Precisione con "%.8f": 1.00000000 | con "%.8e": 1.00000000e-00
Precisione con "%.9f": 1.000000000 | con "%.9e": 1.000000000e-00
Precisione con "%.10f": 1.0000000000 | con "%.10e": 1.0000000000e-00
Precisione con "%.11f": 1.00000000000 | con "%.11e": 1.00000000000e-00
Precisione con "%.12f": 1.000000000000 | con "%.12e": 1.000000000000e-00
Precisione con "%.13f": 1.0000000000000 | con "%.13e": 1.0000000000000e-00
Precisione con "%.14f": 1.00000000000000 | con "%.14e": 1.00000000000000e-00
Precisione con "%.15f": 1.000000000000000 | con "%.15e": 
9.999999999999999e-01
Precisione con "%.16f": 0.9999999999999999 | con "%.16e": 
9.9999999999999989e-01
Precisione con "%.17f": 0.99999999999999989 | con "%.17e": 
9.99999999999999889e-01
Precisione con "%.18f": 0.999999999999999889 | con "%.18e": 
9.999999999999998890e-01
Precisione con "%.19f": 0.9999999999999998890 | con "%.19e": 
9.9999999999999988898e-01
Precisione con "%.20f": 0.99999999999999988898 | con "%.20e": 
9.99999999999999888978e-01
Precisione con "%.21f": 0.999999999999999888978 | con "%.21e": 
9.999999999999998889777e-01
Precisione con "%.22f": 0.9999999999999998889777 | con "%.22e": 
9.9999999999999988897770e-01
Precisione con "%.23f": 0.99999999999999988897770 | con "%.23e": 
9.99999999999999888977698e-01
Precisione con "%.24f": 0.999999999999999888977698 | con "%.24e": 
9.999999999999998889776975e-01
Precisione con "%.25f": 0.9999999999999998889776975 | con "%.25e": 
9.9999999999999988897769754e-01
Precisione con "%.26f": 0.99999999999999988897769754 | con "%.26e": 
9.99999999999999888977697537e-01
Precisione con "%.27f": 0.999999999999999888977697537 | con "%.27e": 
9.999999999999998889776975375e-01
Precisione con "%.28f": 0.9999999999999998889776975375 | con "%.28e": 
9.9999999999999988897769753748e-01
Precisione con "%.29f": 0.99999999999999988897769753748 | con "%.29e": 
9.99999999999999888977697537484e-01
Precisione con "%.30f": 0.999999999999999888977697537484 | con "%.30e": 
9.999999999999998889776975374843e-01
===================
Ciao
-- 
Cybertoldo
Per rispondermi via email, sostituisci moc.liamg con 
gmail.com


Comunque alcuni punti salienti da tenere ben presenti sono:
1) la maggior parte dei numeri decimali, apparentemente innocui,
tipo 1.36 o 0.15, non vengono rappresentati in modo esatto,
perche' codificati in binario risultano numeri periodici, quindi sono
comunque affetti da un errore di rappresentazione
2) a maggior ragione questo vale per numeri irrazionali tipo pi greco o
radice di 7.
3) le operazioni che si effettuano sui numeri in floating point
possono amplificare questi piccoli errori, indipendentemente da quanto
buona sia l'implementazione.
In pratica, se un numero decimale viene rappresentato come
un numero binario periodico, troncato ad una certa cifra, sara',
a seconda di come ho dovuto approssimarlo, un pochino piu' grande
o un pochino piu' piccolo. Questo scostamento  dal valore ideale,
che interessa al massimo l'ultimo bit, puo' pero' venire amplificato
quando si fanno certe operazioni.
Per questo motivo il test di uguaglianza tra numeri in floating point
non si usa quasi mai: due numeri calcolati (idealmente uguali),
ma calcolati con due procedimenti diversi, potrebbero benissimo differire
magari solo nell'ultimo bit, e quindi un test == li dichiarerebbe
diversi.
A me e' capitato addirittura (purtroppo non ho piu' l'esempio sottomano)
che la stessa funzione degli stessi numeri, calcolato nello stesso
modo in due punti diversi di un programma desse valori diversi,
effetto forse di riordinamenti del codice da parte del calcolatore.
Infatti una delle cose da tenere a mente e' che le operazioni
"di macchina" non godono delle classiche proprieta' delle operazioni
matematica corrispondenti. In particolare la proprieta' associativa
e distriutiva vanno allegramente a quel paese. Cioe' se hai
3 numeri A,B,C puo' darsi benissimo che il test (A+B)+C == A+(B+C) fallisca.
Venendo all'esercizio che ti hanno assegnato, se il testo
e' quello che hai riportato, trattasi di un esercizio particolarmente
stupido, perche' chiaramente non puoi
" verificare, mediante un semplice programma, che la formula
sen^2(x)+cos^2(x)=1 sia vera per ogni x reale."
La assoluta stupidita' di chi ha scritto questo esercizio si evince dal fatto
che:
1) non puoi verificarla per ogni x reale perche' i
numeri reali sono infiniti.
2) non puoi verificarla neanche per ogni x "rappresentabile come numero di macchina",
per due motivi: (a) perche' anche se finiti i numeri di macchina double
(io i float preferisco dimenticarmi anche che esistono...) sono troppi per provarli
tutti; (b) perche' per i motivi esposti sopra non e' affatto detto che
sin(x)^2+cos(x)^2 sia sempre uguale a 1. A seconda di come si (s)compensano
gli errori sull'ultimo bit potrebbe benissimo essere uguale a volte ad 1,
e a volte a 1+eps o a 1-eps dove eps e' un numero piccolissimo.
Al massimo l'esercizio potrebbe chiedere di verificare "sperimentalmente"
che per un elevato numero di valori x presi a caso il valore sin(x)^2 + cos(x)^2
risulta molto vicino ad uno....
In generale, per quanto detto, se devi confrontare due numeri X e Y
in floating point, nella maggioranza dei casi (ci sono sempre eccezioni) non
farai X == Y, ma una cosa tipo  fabs(X-Y)< eps,
cioe' andrai a vedere se la differenza in valore assoluto tra X e Y e'
inferiore ad una certa soglia eps da te fissata.
Contrariamente a quello che ti dira' qualcuno non devi fissare eps uguale
alla precisione di macchina, ma tipicamente ad un valore un po' piu' alto.
Questo per 2 motivi principali
1) perche' per effetto dell'amplificazione dell'errore causato dalle
operazioni, se X e Y derivano da lunghi calcoli potrebbero essere idealmente
uguali ma differire comunque non solo nell'ultimo bit significativo,
ma in piu' di un bit.
2) perche', a seconda del problema che stai risolvendo, la soglia per dichiarare
che agli effetti pratici X e' uguale a Y puo' essere diversa.
Se ad esempio la differenza tra X e Y va a finire al denominatore di una
divisione, se questa differenza e' maggiore di zero ma molto piccola
potrei comunque evitare un errore a runtime (divisione per zero) ma magari
ottenere comunque un risultato quasi privo di senso, etc.etc.
scusa la sbrodolata,
ciao,
g.