Come posso confrontare correttamente i doppi valori per l'uguaglianza in un test unitario?


20

Di recente ho progettato un modulo di serie storiche in cui le mie serie storiche sono essenzialmente a SortedDictionnary<DateTime, double>.

Ora vorrei creare unit test per assicurarsi che questo modulo funzioni sempre e produca il risultato atteso.

Un'operazione comune è calcolare la prestazione tra i punti nelle serie temporali.

Quindi quello che faccio è creare una serie temporale con, diciamo, {1.0, 2.0, 4.0} (in alcune date), e mi aspetto che il risultato sia {100%, 100%}.

Il fatto è che se creo manualmente una serie temporale con i valori {1.0, 1.0} e controllo l'uguaglianza (confrontando ogni punto), il test non passerebbe, poiché ci saranno sempre imprecisioni quando si lavora con rappresentazioni binarie di reali numeri.

Quindi, ho deciso di creare la seguente funzione:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

Esiste un altro modo comune di trattare un caso del genere?

Risposte:


10

Posso pensare ad altri due modi per affrontare questo problema:

Puoi usare Is.InRange:

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

Puoi usare Math.Round:

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

Penso che entrambi i modi siano più espressivi di una funzione dedicata, perché il lettore può vedere esattamente cosa sta succedendo con il tuo numero prima che venga confrontato con il valore atteso.


2
Solo una nota che questa risposta è specifica di NUnit e mostra il modello di asserzione "basato su vincoli". Il classico modello di asserzione sarebbe simile a: Assert.AreEqual (previsto, effettivo, tolleranza);
RichardM,

1
@RichardM: pubblica questo come risposta e lo selezionerò, lo accetterò.
SRKX

La risposta di @dasblinkenlight è corretta, aggiungendo solo alcuni dettagli (poiché potrebbe non essere chiaro - il modello di asserzione classico è anche NUnit). Altri framework di test (non MSTest) probabilmente hanno un proprio modello di assert per gestire valori in virgola mobile.
RichardM,

1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));fallirà se tolerance/abs(expected) < 1E-16.
quant_dev

@quant_dev Hai perfettamente ragione. Poiché OP parla del calcolo dei rendimenti in percentuale, ho ipotizzato che le abs(expected)cifre fossero singole o doppie. Ho anche assunto la tolleranza in prossimità di 1E-9. In base a questi presupposti, questo approccio certamente semplicistico potrebbe servirti ragionevolmente bene (lo uso Is.InRangenei miei test).
dasblinkenlight,


3

Dipende da cosa fai con i numeri. Se si sta testando un metodo che dovrebbe ad esempio selezionare un valore appropriato da un set di input basato su alcuni criteri, è necessario verificare l'uguaglianza rigorosa. Se si eseguono calcoli in virgola mobile, in genere è necessario eseguire il test con una tolleranza diversa da zero. Quanto è grande la tolleranza dipende dai calcoli, ma con doppia precisione un buon punto di partenza è scegliere la tolleranza relativa 1E-14 per i calcoli semplici e 1E-8 (tolleranza) per quelli più complicati. YMMV ovviamente, ed è necessario aggiungere una piccola tolleranza assoluta se il risultato atteso è 0.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.