Il modo giusto per confrontare un System.Double a '0' (un numero, int?)


87

Scusa, questa potrebbe essere una domanda facile e stupida, ma devo saperlo per esserne sicuro.

Ho questa ifespressione,

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

Quell'espressione è uguale a

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

? Perché allora potrei avere un problema, inserendo ifad esempio un valore di 0.9.


3
// Comparison of floating point numbers with equality // operator. Hai davvero bisogno di specificarlo? :)
George Johnston

1
Diamine no. Ci sono tantissimi valori tra 0 e 1. Perché non provarlo e vedere di persona?
Igby Largeman

12
Ho appena scritto la stessa cosa di Resharper, per mostrare dove si trova il mio obiettivo.
radbyx

@Charles: Inoltre, ci sono molti numeri inferiori a 0.
Brian

possibile duplicato di Confronto dei valori doppi in C #
Dzyann

Risposte:


114

Bene, quanto vicino hai bisogno che il valore sia 0? Se si eseguono molte operazioni in virgola mobile che in "precisione infinita" potrebbero risultare in 0, si potrebbe ottenere un risultato "molto vicino" a 0.

In genere in questa situazione si desidera fornire una sorta di epsilon e verificare che il risultato sia solo all'interno di tale epsilon:

if (Math.Abs(something) < 0.001)

L'epsilon che dovresti usare è specifico dell'applicazione, dipende da cosa stai facendo.

Ovviamente, se il risultato dovrebbe essere esattamente zero, allora un semplice controllo di uguaglianza va bene.


In realtà, ho bisogno che sia esattamente zero, come sarebbe? Ho cercato Double.Zero, ma conosco fortuna. C'è un diritto costante? A proposito, grazie, ora ho la parte epsilon :)
radbyx

24
@radbyx: basta usare == 0. Hai un letterale lì - è abbastanza costante :)
Jon Skeet

35

Se somethingè stato assegnato dal risultato di un'operazione diversa da something = 0allora è meglio usare:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

Modifica : questo codice è sbagliato. Epsilon è il numero più piccolo, ma non del tutto zero. Quando desideri confrontare un numero con un altro, devi pensare a quale sia la tolleranza accettabile. Diciamo che qualsiasi cosa oltre a .00001 non ti interessa. Questo è il numero che useresti. Il valore dipende dal dominio. Tuttavia, per lo più certamente non è mai Double.Epsilon.


2
Questo non risolve il problema di arrotondamento, per esempio Math.Abs(0.1f - 0.1d) < double.Epsilonèfalse
Thomas Lule

6
Double.Epsilon è troppo piccolo per tale confronto. Double.Epsilon è il numero positivo più piccolo che double può rappresentare.
Evgeni Nabokov,

3
Questo non ha senso perché è praticamente lo stesso del confronto con 0. -1
Gaspa79

1
L'obiettivo è confrontare con il concetto di 0 senza utilizzare ==. Almeno fa senso matematicamente. Suppongo che tu abbia un doppio a portata di mano e che tu voglia confrontarlo con il concetto di zero senza ==. Se il tuo double è diverso da 0d per qualsiasi motivo, incluso l'arrotondamento, il riempimento di prova restituisce false. Questo confronto sembra valido per qualsiasi double e restituirà true solo se questo double è il più piccolo del numero più piccolo che può essere rappresentato, che sembra una buona definizione per testare il concetto di 0, no?
sonatique

3
@MaurGi: ti sbagli: double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
sonatique

26

Il tuo somethingè un doublee lo hai identificato correttamente nella riga

if (something == 0)

abbiamo una doublea sinistra (sx) e una inta destra (dx).

Ma ora sembra che pensi che lhs verrà convertito in un int, e quindi il ==segno confronterà due numeri interi. Questo è non è quello che succede. La conversione da double a int è esplicita e non può avvenire "automaticamente".

Invece accade il contrario. La rhs viene convertita in doublee quindi il ==segno diventa un test di uguaglianza tra due doppi. Questa conversione è implicita (automatica).

È considerato migliore (da alcuni) scrivere

if (something == 0.0)

o

if (something == 0d)

perché poi è immediato che stai confrontando due doppie. Tuttavia, è solo una questione di stile e leggibilità perché il compilatore farà comunque la stessa cosa.

È anche rilevante, in alcuni casi, introdurre una "tolleranza" come nella risposta di Jon Skeet, ma anche quella tolleranza lo sarebbe double. Si potrebbe essere, naturalmente, 1.0se si voleva, ma non deve essere [il meno strettamente positiva] intero.


17

Se desideri semplicemente sopprimere l'avviso, procedi in questo modo:

if (something.Equals(0.0))

Naturalmente, questa è una soluzione valida solo se sai che la deriva non è un problema. Lo faccio spesso per verificare se sto per dividere per zero.


4

Onestamente, non credo che sia uguale. Considera il tuo esempio: qualcosa = 0,9 o 0,0004. Nel primo caso sarà FALSO, nel secondo caso sarà VERO. Trattando questo tipo di solito definisco per me la percentuale di precisione e confronto all'interno di quella precisione. Dipende dalle tue esigenze. qualcosa di simile a...

if(((int)(something*100)) == 0) {


//do something
}

Spero che sia di aiuto.


2
qualcosa deve essere esattamente zero.
radbyx

quindi sei fortunato, in questo caso :)
Tigran

3

Ecco l'esempio che presenta il problema (preparato in LinQPad - se non lo hai usa solo al Console.Writelineposto del Dumpmetodo):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

Sia x che y sono teoricamente uguali e uguali a: 0,00001 ma a causa della mancanza di "precisione infinita" questi valori sono leggermente diversi. Sfortunatamente abbastanza leggermente da tornare falsequando si confronta a 0 nel solito modo.

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.