Domanda

Questo codice funziona (C # 3)

double d;
if(d == (double)(int)d) ...;
  1. C'è un modo migliore per farlo?
  2. Per motivi estranei voglio evitare così il doppio cast; che bei modi esistono oltre a questo? (anche se non sono così buoni)

Nota: Diverse persone hanno sottolineato il punto (importante) che == è spesso problematico in termini di virgola mobile. In questi casi mi aspetto valori nell'intervallo da 0 a qualche centinaio e si suppone che siano numeri interi (i non ints sono errori) quindi se quei punti & Quot; non dovrebbero & Quot; essere un problema per me.

È stato utile?

Soluzione

d == Math.Floor(d)

fa la stessa cosa in altre parole.

NB: Spero che tu sappia che devi fare molta attenzione quando fai questo tipo di cose; float / doppi accumuleranno molto facilmente errori minuscoli che fanno fallire confronti esatti (come questo) senza motivo ovvio.

Altri suggerimenti

Se il tuo doppio è il risultato di un altro calcolo, probabilmente vuoi qualcosa del tipo:

d == Math.Floor(d + 0.00001);

In questo modo, se si è verificato un leggero errore di arrotondamento, corrisponderà comunque.

Penso che funzionerebbe:

if (d % 1 == 0) {
  //...
}

Non posso rispondere alla parte specifica della domanda in C #, ma devo sottolineare che probabilmente manca un problema generico con numeri in virgola mobile.

Generalmente, l'intelligenza non è ben definita sui float. Per lo stesso motivo che l'uguaglianza non è ben definita sui float. I calcoli in virgola mobile normalmente includono errori di arrotondamento e di rappresentazione.

Ad esempio, 1.1 + 0.6 != 1.7.

Sì, è così che funzionano i numeri in virgola mobile.

Qui, 1.1 + 0.6 - 1.7 == 2.2204460492503131e-16.

A rigor di termini, la cosa più vicina al confronto di uguaglianza che puoi fare con i float è confrontarli fino ad una precisione scelta .

Se ciò non è sufficiente, è necessario lavorare con una rappresentazione numerica decimale, con una rappresentazione numerica in virgola mobile con intervallo di errori incorporato o con calcoli simbolici.

Se hai intenzione di convertirlo, la risposta di Mike F / Khoth è buona, ma non risponde alla tua domanda. Se hai intenzione di testare ed è effettivamente importante, ti consiglio di implementare qualcosa che includa un margine di errore.

Ad esempio, se stai considerando il denaro e vuoi testare anche gli importi in dollari, potresti dire (seguendo il modello di Khoth):

if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)

In altre parole, prendi il valore assoluto della differenza del valore e la sua rappresentazione intera e assicurati che sia piccola.

Non è necessario il extra (doppio) lì dentro. Questo funziona:

if (d == (int)d) {
 //...
}

Usa Math.Truncate ()

Un semplice test come 'x == floor (x)' è matematicamente garantito per funzionare correttamente, per qualsiasi numero FP a precisione fissa.

Tutte le codifiche FP a precisione fissa legali rappresentano numeri reali distinti, quindi per ogni numero intero x esiste al massimo una codifica FP a precisione fissa che corrisponde esattamente.

Pertanto, per ogni intero x che PUO 'essere rappresentato in questo modo, abbiamo x == floor (x) necessariamente, poiché floor (x) per definizione restituisce il numero FP più grande y tale che y < = xey rappresenta un numero intero; quindi floor (x) deve restituire x.

Questo ti permetterà di scegliere quale precisione stai cercando, più o meno mezzo segno di spunta, per tenere conto della deriva in virgola mobile. Anche il confronto è integrale, il che è carino.

static void Main(string[] args)
{
    const int precision = 10000;

    foreach (var d in new[] { 2, 2.9, 2.001, 1.999, 1.99999999, 2.00000001 })
    {
        if ((int) (d*precision + .5)%precision == 0)
        {
            Console.WriteLine("{0} is an int", d);
        }
    }
}

e l'output è

2 is an int
1.99999999 is an int
2.00000001 is an int

Qualcosa del genere

double d = 4.0;
int i = 4;

bool equal = d.CompareTo(i) == 0; // true

Potresti usare questo

    bool IsInt(double x)
    {
        try
        {
            int y = Int16.Parse(x.ToString());
            return true;
        }
        catch 
        {
            return false;
        }
    }

Per gestire la precisione del doppio ...

Math.Abs(d - Math.Floor(d)) <= double.Epsilon

Considera il seguente caso in cui un valore inferiore a double.Epsilon non riesce a confrontare come zero.

// number of possible rounds
const int rounds = 1;

// precision causes rounding up to double.Epsilon
double d = double.Epsilon*.75;

// due to the rounding this comparison fails
Console.WriteLine(d == Math.Floor(d));

// this comparison succeeds by accounting for the rounding
Console.WriteLine(Math.Abs(d - Math.Floor(d)) <= rounds*double.Epsilon);

// The difference is double.Epsilon, 4.940656458412465E-324
Console.WriteLine(Math.Abs(d - Math.Floor(d)).ToString("E15"));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top