The Float Precision problema
Hai due problemi qui, ma entrambi provengono dalla stessa radice
Non si può paragonare con precisione galleggianti. Non puoi sottrarre o dividere in modo preciso. Non puoi contare esattamente per loro. Qualsiasi operazione con loro potrebbe (e quasi sempre fa) portare qualche errore nel risultato. Anche a=0.2f
non è un'operazione precisa. Le ragioni più profonde di ciò sono spiegate molto bene dagli autori delle altre risposte qui. (I miei ringraziamenti e voti a loro per quello.)
Ecco il primo e più semplice errore. Si dovrebbe mai, mai , mai, mai, MAI uso su di loro == o il suo equivalente in qualsiasi lingua.
Invece di a==b
, utilizzare invece Abs(a-b)<HighestPossibleError
.
Ma questo non è l'unico problema nel vostro compito.
Abs(1/y-x)<HighestPossibleError
non funziona. Almeno, non funzionerà abbastanza spesso. Perché?
Prendiamo la coppia x = 1000 ey = 0,001. Prendiamo l'errore relativo "iniziale" di y per 10 -6.
(Errore relativo = errore/valore).
Gli errori relativi dei valori si aggiungono a moltiplicazione e divisione.
1/y è circa 1000. Il suo errore relativo è lo stesso 10 -6. ("1" non ha errori)
Ciò rende l'errore assoluto = 1000 * 10 -6 = 0,001. Quando sottrassi x più tardi, quell'errore sarà tutto ciò che rimane. (Gli errori assoluti si aggiungono all'aggiunta e alla sottrazione e l'errore di x è trascurabilmente piccolo.) Sicuramente, non contano su così grandi errori, HighestPossibleError sarebbe sicuramente impostata più bassa e il programma sarebbe buttare fuori un buon paio di x, y
Così, i due successivi regola per le operazioni galleggiante: non cercate di dividere una maggiore valuer by lesser one e Dio ti salvi di sottrarre i valori vicini dopo.
Esistono due modi semplici per evitare questo problema.
Fondando quello di x, y ha il maggior valore addominali e dividere 1 dalla maggiore uno e solo successivamente per sottrarre il minore uno.
Se si desidera confrontare 1/y against x
, mentre si sta lavorando ancora con le lettere, non i valori, e le operazioni non fare errori, si moltiplicano entrambi i lati del confronto da y e hai 1 against x*y
. (Di solito dovresti controllare i segni in quell'operazione, ma qui usiamo valori abs, quindi, è pulito.) Il confronto dei risultati non ha alcuna divisione.
In una via più breve:
1/y V x <=> y*(1/y) V x*y <=> 1 V x*y
Sappiamo già che tale confronto come 1 against x*y
dovrebbe essere fatto in modo da:
const float HighestPossibleError=1e-10;
if(Abs(x*y-1.0)<HighestPossibleError){...
Questo è tutto.
P.S. Se si ha realmente bisogno tutto su una riga, utilizzare:
if(Abs(x*y-1.0)<1e-10){...
Ma è cattivo stile. Non lo consiglierei.
P.P.S. Nel secondo esempio il compilatore ottimizza il codice in modo tale da impostare z a 5 prima di eseguire qualsiasi codice. Quindi, il controllo di 5 contro 5 funziona anche per i galleggianti.
http://docs.oracle.com/ cd/E19957-01/806-3568/ncg_goldberg.html – Mysticial
'((x * y) == 1)' non funziona? – Vyktor
Ho aggiunto alcune informazioni nella mia risposta. E +1 per una domanda fruttuosa. – Gangnus