Fa “Epsilon” garantisce davvero nulla nei calcoli in virgola mobile?
-
02-10-2019 - |
Domanda
Per rendere il problema a breve diciamo che voglio calcolare l'a / (b - c)
espressione sul float
s.
Per assicurarsi che il risultato è significativo, posso verificare se b
e c
sono in egual:
float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
return a / (b - c);
}
ma i miei test dimostrano che non è sufficiente a garantire sia risultati significativi né non mancando di fornire un risultato, se è possibile.
Caso 1:
a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;
Risultato:. Il caso condizione non è soddisfatta, ma l'espressione produrrebbe un risultato corretto 100.000.008 (come per la precisione dei galleggianti)
Caso 2:
a = 1e33f;
b = 0.000003;
c = 0.000002;
Risultato:. La condizione if è soddisfatta, ma l'espressione non produce un significativo risultato +1.#INF00
L'ho trovato molto più affidabile per controllare il risultato, non gli argomenti:
const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
return x;
}
Ma quello che per è l'Epsilon allora e perché tutti dicendo Epsilon è bene utilizzare?
Soluzione
"è necessario utilizzare un epsilon quando si tratta di carri" è una reazione istintiva di programmatori con una comprensione superficiale di calcoli in virgola mobile, per i confronti in generale (non solo a zero).
Questo è di solito inutile perché non vi dice come ridurre al minimo la propagazione di errori di arrotondamento, ma non dice come evitare problemi di cancellazione o di assorbimento, e anche quando il problema è infatti legato al confronto dei due galleggianti, che non ti dicono quale valore di Epsilon è giusto per quello che state facendo .
Se non avete letto Ciò che ogni computer Scientist deve sapere a proposito di virgola mobile aritmetica , è un buon punto di partenza. Oltre che, se si è interessati alla precisione del risultato della divisione nel tuo esempio, è necessario stimare quanto b-c
impreciso è stata fatta da precedente errori di arrotondamento, perché in effetti se b-c
è piccolo, un piccolo errore assoluto corrisponde in larga errore assoluto sul risultato. Se la vostra preoccupazione è solo che la divisione non dovrebbe troppo pieno, quindi il test (il risultato) è giusto. Non v'è alcun motivo di prova per un divisore nulla con numeri in virgola mobile, basta provare per trabocco del risultato, che cattura entrambi i casi in cui il divisore è nullo e in cui il divisore è così piccola da rendere il risultato non rappresentabili con precisione.
Per quanto riguarda la propagazione di errori di arrotondamento, esiste Gli analizzatori specializzati che può aiutare a stimare, perché è una cosa noiosa da fare a mano.
Altri suggerimenti
Epsilon viene utilizzato per determinare se due numeri soggetti a errore di arrotondamento sono abbastanza vicino per essere considerato "uguale". Si noti che è meglio di prova fabs(b/c - 1) < EPS
di fabs(b-c) < EPS
, e ancora meglio - grazie al design di IEEE galleggia - a prova di abs(*(int*)&b - *(int*)&c) < EPSI
(dove EPSI qualche piccolo intero)
Il tuo problema è di natura diversa, e probabilmente garantisce la verifica del risultato, piuttosto che gli ingressi.