Come determinare se un decimale / doppio è un numero intero?


225

Come faccio a sapere se un valore decimale o doppio è un numero intero?

Per esempio:

decimal d = 5.0; // Would be true
decimal f = 5.5; // Would be false

o

double d = 5.0; // Would be true
double f = 5.5; // Would be false

Il motivo per cui vorrei saperlo è che posso determinare a livello di codice se voglio produrre il valore usando .ToString("N0")o .ToString("N2"). Se non esiste un valore in virgola decimale, non voglio mostrarlo.

Risposte:


410

Per i numeri in virgola mobile, n % 1 == 0è in genere il modo per verificare se c'è qualcosa oltre il punto decimale.

public static void Main (string[] args)
{
    decimal d = 3.1M;
    Console.WriteLine((d % 1) == 0);
    d = 3.0M;
    Console.WriteLine((d % 1) == 0);
}

Produzione:

False
True

Aggiornamento: Come indicato da @Adrian Lopez di seguito, il confronto con un valore piccolo eliminerà i calcoli errati diepsiloncalcolo in virgola mobile. Poiché la domanda riguarda idoublevalori, di seguito verrà fornita una risposta di prova di calcolo in virgola mobile più:

Math.Abs(d % 1) <= (Double.Epsilon * 100)

96
Funziona quando il numero inizia come un numero intero, ma non necessariamente quando il numero è il risultato di un calcolo in virgola mobile. Che ne dici di qualcosa come "(d% 1) <epsilon" dove epsion ha un piccolo valore?
Adrian Lopez,

9
È un peccato che la risposta migliore in questa discussione sia un commento, piuttosto che la risposta accettata. Bello Adrian.
starskythehutch il

13
Penso anche che il commento di Adrian sopra sia la migliore risposta. Per inserire i suoi consigli nel codice formale C #: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Fai qualcosa se n è intero}.
Ruben Ramirez Padron,

7
IMHO, l'operatore del modulo e i numeri in virgola mobile non si mescolano in alcun modo utili. Il codice suggerito è incredibilmente confuso dato il numero di sequenze di tasti utilizzate e non funzionerà quasi da nessuna parte al di fuori dei linguaggi .NET. Scommetto che è anche molto più lento del modo corretto (che non usa alcuna divisione). Il modo corretto per farlo è usare Math.Abs(d-(int)d) < double.Epsilon. Come avremmo dovuto imparare tutti nella prima settimana della nostra prima lezione di programmazione al college.
krowe2,

9
In realtà, come indicato nella domanda, questa risposta è corretta e i commenti sono sbagliati. L'OP non vuole sapere se un doppio è un numero intero a fini matematici, ma piuttosto come visualizzarlo. Solo i valori interi esatti devono essere visualizzati senza un punto decimale. Inoltre, il commento su mod che non è utile con virgola mobile e non funziona al di fuori di .NET non è ben informato. Ed (int)dè un disastro che genererà un'eccezione per la maggior parte dei doppi valori.
Jim Balter,

49

Ci sono molti modi per farlo. Per esempio:

double d = 5.0;
bool isInt = d == (int)d;

Puoi anche usare il modulo.

double d = 5.0;
bool isInt = d % 1 == 0;

Uno di questi sarebbe più veloce dell'altro? Voglio farlo in un contesto sensibile alle prestazioni.
Basilio

@Basil: dipende dalle circostanze. Dovresti fare dei tempi per te stesso e giudicare.
Erik Funkenbusch,

4
Math.Abs(d-(int)d) < double.Epsilonè più sicuro did == (int)d
Reactgular

3
@MathewFoscarini - Penso che tu sia confuso. Lo imposta su false, perché il risultato di 16.1 - 6.1 non è un int. Il punto era scoprire se un dato valore è un int, non se qualcosa che è approssimativamente un int è un int.
Erik Funkenbusch,

1
@MathewFoscarini - Sì, un int è un numero senza un valore decimale (o un valore decimale di 0). 16.1-6.1 non crea un valore decimale 0, è un valore non zero molto piccolo causato dalle stranezze del formato IEEE in virgola mobile. Non c'è modo di sapere se il numero è SUPPOSTO per avere un valore decimale o meno, quindi supporre che un valore di arrotondamento sia altrettanto impreciso. Lo scopo della domanda era sapere se un numero in virgola mobile era un numero intero, non se fosse approssimativamente un numero intero.
Erik Funkenbusch,

21

Cosa ne pensi di questo?

public static bool IsInteger(double number) {
    return number == Math.Truncate(number);
}

Stesso codice per decimal.

Mark Byers ha fatto un buon punto, in realtà: questo potrebbe non essere quello che vuoi davvero . Se quello che davvero interessa è se un numero arrotondato alle due decimali più vicine sia un numero intero , puoi invece farlo:

public static bool IsNearlyInteger(double number) {
    return Math.Round(number, 2) == Math.Round(number);
}

1
forse aggiorna la tua soluzione e aggiungi: && number <int.MaxValue && number> int.MinValue
Walter Vehoeven

12

Mentre le soluzioni proposte sembrano funzionare per semplici esempi, farlo in generale è una cattiva idea. Un numero potrebbe non essere esattamente un numero intero ma quando provi a formattarlo, è abbastanza vicino a un numero intero che ottieni1.000000 . Questo può accadere se si esegue un calcolo che in teoria dovrebbe fornire esattamente 1, ma in pratica fornisce un numero molto vicino ma non esattamente uguale a uno a causa di errori di arrotondamento.

Invece, formattalo prima e se la stringa termina in un punto seguito da zeri, quindi rimuovili. Ci sono anche alcuni formati che puoi usare automaticamente per eliminare gli zeri finali. Questo potrebbe essere abbastanza buono per il tuo scopo.

double d = 1.0002;
Console.WriteLine(d.ToString("0.##"));
d = 1.02;
Console.WriteLine(d.ToString("0.##"));

Produzione:

1
1.02

@Mark Sembra interessante. Hai un esempio di un formato che rimuove gli zeri finali?
Jim Geurts,

Concordo sul fatto che sia più sicuro e che cosa dovrebbe probabilmente fare l'OP, ma non è una risposta alla domanda più ristretta (ma più interessante) se un valore ha una parte frazionaria o meno.
Clifford,

3
@Clifford: di solito cerco di rispondere in base a ciò che è meglio per risolvere il problema dei PO, non in base a ciò che dice il titolo. I titoli raramente sono una descrizione accurata del problema.
Mark Byers,

+1 Concordo sul fatto che provare a testare float o doppi per vedere se potrebbero essere ints è male a causa di arrotondamenti ed errori di precisione.
Romain Hippeau

1
Per l'utilizzo del denaro, probabilmente si desidera visualizzare 1.2 come 1.20, il che non è il caso della soluzione suggerita. Qualche acquirente?
Kjell Rilbe,

10
bool IsInteger(double num) {
    if (ceil(num) == num && floor(num) == num)
        return true;
    else
        return false;
}

Problemo solvo.

Modifica: presentato da Mark Rushakoff.


4
o semplicementereturn ceil(num) == num && floor(num) == num;
Brian Rasmussen,

13
o semplicementereturn ceil(num) == floor(num);
Gregregdennis il

4

La risposta di Mark Rushakoff potrebbe essere più semplice, ma anche i seguenti funzionano e possono essere più efficienti poiché non vi sono operazioni di divisione implicite:

     bool isInteger = (double)((int)f) == f ;

e

     bool isInteger = (decimal)((int)d) == d ;

Se vuoi una singola espressione per entrambi i tipi, forse

     bool isInteger = (double)((int)val) == (double)val ;

4

Se il limite superiore e inferiore delle Int32materie:

public bool IsInt32(double value)
{
    return  value >= int.MinValue && value <= int.MaxValue && value == (int)value;
}

Prima prova, poi cast in questo modo genererebbe un'eccezione diversa da return false, forse aggiorna la tua risposta
Walter Vehoeven,

@computer, sì buon punto. Per quanto riguarda il lancio sul cast, immagino che dipenda dalle impostazioni del progetto.
nawfal,

3
static bool IsWholeNumber(double x) 
{
    return Math.Abs(x % 1) < double.Epsilon;
}

2

È possibile utilizzare la formattazione String per il doppio tipo. Ecco un esempio:

double val = 58.6547;
String.Format("{0:0.##}", val);      
//Output: "58.65"

double val = 58.6;
String.Format("{0:0.##}", val);      
//Output: "58.6"

double val = 58.0;
String.Format("{0:0.##}", val);      
//Output: "58"

Fammi sapere se questo non aiuta.


1
Questo non affronta realmente la questione di determinare se un valore non ha una parte frazionaria, che è una domanda matematica. Tuttavia, è probabilmente ciò di cui l'OP ha bisogno in considerazione della sua nota esplicativa.
Clifford,

2
Sì, vuole solo formattare un valore doppio o decimale senza virgola decimale. Grazie ...
BALKANGraph


0

Ho affrontato una situazione simile, ma in cui il valore è una stringa. L'utente digita un valore che dovrebbe essere un importo in dollari, quindi voglio confermare che è numerico e ha al massimo due cifre decimali.

Ecco il mio codice per restituire true se la stringa "s" rappresenta un valore numerico con al massimo due cifre decimali e false in caso contrario. Evita qualsiasi problema derivante dall'imprecisione dei valori in virgola mobile.

try
{
    // must be numeric value
    double d = double.Parse(s);
    // max of two decimal places
    if (s.IndexOf(".") >= 0)
    {
        if (s.Length > s.IndexOf(".") + 3)
            return false;
    }
    return true;
catch
{
    return false;
}

Ne discuterò in modo più dettagliato su http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Questo presuppone che tu stia lavorando con una cultura. Ad esempio, non funzionerebbe correttamente con culture che rappresentano decimali come 1.000,00
Jim Geurts,

0

L'uso di int.TryParse produrrà questi risultati:

        var shouldBeInt = 3;

        var shouldntBeInt = 3.1415;

        var iDontWantThisToBeInt = 3.000f;

        Console.WriteLine(int.TryParse(shouldBeInt.ToString(), out int parser)); // true

        Console.WriteLine(int.TryParse(shouldntBeInt.ToString(), out parser)); // false

        Console.WriteLine(int.TryParse(iDontWantThisToBeInt.ToString(), out parser)); // true, even if I don't want this to be int

        Console.WriteLine(int.TryParse("3.1415", out  parser)); // false

        Console.WriteLine(int.TryParse("3.0000", out parser)); // false

        Console.WriteLine(int.TryParse("3", out parser)); // true

        Console.ReadKey();

-2

Forse non è la soluzione più elegante ma funziona se non sei troppo esigente!

bool IsInteger(double num) {
    return !num.ToString("0.################").Contains(".");
}

8
Questa è una soluzione terribile
Ospho,

-2

È possibile utilizzare il metodo "TryParse".

int.TryParse()

Questo verifica se il valore può essere convertito in un valore intero numero intero. Il risultato può quindi indicare una bandiera che può essere utilizzata altrove nel codice.


L'argomento per int.TryParse è una stringa, non un doppio.
Jim Balter,

yourDouble.toString("G17")
BackDoorNoBaby

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.