2012-12-19 10 views
17

Considerate le dichiarazioniConversione da virgola mobile a doppia: la migliore affermazione in un test di unità?

float f = 7.1f; 
double d = f; 

Quello che possiamo affermare in una prova di unità su d?


Per esempio, questo non funziona:

Console.WriteLine(d == 7.1d); // false 
Console.WriteLine(d < 7.1d + float.Epsilon); // true by luck 
Console.WriteLine(d > 7.1d - float.Epsilon); // false (less luck) 

Il modo migliore che ho trovato finora è quello di convertire il valore indietro:

float f2 = (float)d; 
Console.WriteLine(f2 == f); // true 

Quale sarebbe lo stesso come il bruto modo per dire

Console.WriteLine(d == 7.1f); // 7.1f implicitly converted to double as above 

Questa domanda NON riguarda la precisione del doppio e del float in generale, ma SOLO la questione pragmatica su come un test unitario possa descrivere al meglio i confini di d. Nel mio caso, d è il risultato di una conversione che si verifica nel codice generato dalla generazione di codice leggero. Mentre collaudo questa generazione di codice, devo fare delle asserzioni sul risultato di questa funzione e questo si riduce alla semplice domanda precedente.

+0

Quale framework di test dell'unità stai utilizzando? Potresti non necessariamente far valere una condizione che ne deriva, ad es. 'Assert.IsTrue (condition)', potresti essere in grado di utilizzare per es. 'Assert.AreEqual (value1, value2)' che può gestire l'equivalenza tra i formati numerici. – StuperUser

+0

Non sono ancora sicuro di cosa stai provando a testare qui. Un doppio ha più precisione di un galleggiante e quindi non saranno quasi mai "uguali" tra loro. – CodeCaster

+0

Se ometti il ​​finale f nella costante usata per inizializzare il float, potrebbe essere anche peggio, vedi un esempio qui http://stackoverflow.com/questions/13276862/cc-notation-of-double-floating-point -values ​​/ 13279512 # 13279512 –

risposta

4

Il "modo migliore" è affermare che il codice generato restituisce qualcosa che è, entro il margine di errore float, 7.1. Questo può essere ciò che vuoi controllare, nel qual caso continuare.

D'altra parte, si potrebbe desiderare di affermare che il codice generato ritorna specificamente il risultato della fusione 7.1f ad un double, nel qual caso si potrebbe fare:

Console.WriteLine(d == (double)f); 

Questo è più stringente - il tuo test asserisce che d si trova entro un intervallo ristretto, mentre il test precedente afferma che d è un valore specifico.

Dipende molto da cosa si utilizzerà d per. Se si tratta di un caso in cui le cose andranno storte se non è il valore esatto, testare il valore esatto, ma se è OK essere all'interno di un valore float, controllare lo float.

+0

Rawling, è tutto per me. Mi aspetto che il mio codice converta 7.1f in un double, quindi la mia affermazione ora è d.Should(). Be ((double) 7.1f); che rende più chiara la mia aspettativa. grazie per il tuo contributo. – citykid

1

per confrontare i valori di due punti galleggiante ibm sugests testare abs(a/b - 1) < epsilon

un msnd afferma che Epsilon proprietà riflette il più piccolo valore positivo che è rilevante in operazioni numeriche o confronti quando il valore dell'istanza è zero.

quindi in realtà si dovrebbe verificare

Math.Abs(d/(double)f) - 1) < float.Epsilon) 
+0

thx per il suggerimento da ibm: se non si conosce la scala delle misure sottostanti, utilizzando il test "abs (a/b - 1) citykid

+0

+1. Sì, non ha senso aggiungere epsilon a un numero possibilmente grande. Non avrebbe alcun effetto. Questo funzionerebbe solo con numeri fissi. –

+0

Il documento a cui si fa riferimento è pubblicato da IBM, ma è identificato come prodotto da una persona e società diversa da IBM. Quindi non è chiaro che IBM stia facendo quel suggerimento, più di quanto un editore di libri sia d'accordo con tutto ciò che gli autori pubblicano. E non è del tutto corretto accusare IBM di promuovere questa pratica sciatta basata su questo. –

1

(float) d == f.

Un'altra risposta suggerita d == (double) f, ma questo è un test inutile perché (double) f esegue la stessa conversione che esegue d = f implicitamente. Quindi l'unica cosa che questa asserzione potrebbe essere testata è se alcuni aspetti dell'implementazione siano infranti (ad es., il compilatore ha implementato una delle conversioni in modo errato e diverso dall'altra), alcuni meccanismi esterni sono stati modificati d o f tra l'assegnazione e l'asserzione, oppure il codice sorgente è stato interrotto in modo che d non era né doublefloat né alcun tipo che può contenere esattamente il valore di f o che l'assegnazione d = f non è stata eseguita.

Generalmente, non ci aspettiamo errori in virgola mobile, perché, in ogni normale implementazione di virgola mobile, la conversione da una precisione più stretta a una più ampia precisione della stessa radice non ha errori, poiché la precisione più ampia può rappresentare ogni valore la precisione più stretta possibile. In situazioni non comuni, un formato a virgola mobile più ampio potrebbe avere un intervallo di esponenti più piccolo. Solo in questo caso, o in formati a virgola mobile definiti perversamente, la conversione in un formato più ampio può causare una variazione di valore. In questi casi, l'esecuzione della stessa conversione non rileva la modifica.

Invece, convertiamo dal formato più ampio nuovamente nel formato più stretto. Se d differisce da f, questa conversione ha la possibilità di rilevare l'errore. Ad esempio, supponiamo che f contenga 0x1p-1000, ma, per qualche ragione, non è rappresentabile nel formato di d, quindi è stato arrotondato a zero. Quindi (float) d == f restituisce (float) 0 == 0x1p-1000, quindi 0 == 0x1p-1000, quindi false. Inoltre, questo test può rilevare gli stessi errori dell'altro suggerimento: un'implementazione non funzionante, l'alterazione di d o f, un tipo errato di d e un'assegnazione mancante di d = f.

A parte questo, quali errori vorresti rilevare con un'asserzione qui?

+0

Vale la pena notare che la conversione da 'float' a' Decimal' o 'double' a' Decimal' può essere lossy * anche per valori che sono rappresentabili con precisione in entrambi i formati *. Ad esempio, la conversione di '16777215f' in' Decimale' produce un valore di 16777220, anche se 'float' rappresenta precisamente il valore 16,777,215, e un' Decimal' può anche contenere quel valore. – supercat

+0

@supercat: "... in ogni normale implementazione, la conversione in una precisione più ampia ** della stessa radice ** non ha errori ...". –

+0

È vero che non ci si può aspettare che ogni valore 'float' sia * rappresentabile * in' Decimale', dato che usano una radix diversa, e certamente non ci si può aspettare che una conversione sia precisa nei casi in cui il formato di destinazione non ha rappresentazioni per il valore in questione. La mia intenzione non era quella di contraddirti, ma piuttosto di sottolineare che se le radix differiscono, anche i valori che sono precisamente rappresentabili nei vecchi e nuovi formati possono essere convertiti in modo strano (forse l'arrotondamento di 16777215f a 16777220m è documentato, ma a dir poco strano). – supercat

Problemi correlati