Comment déterminer si une décimale / double est un entier?


225

Comment savoir si une valeur décimale ou double est un entier?

Par exemple:

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

ou

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

La raison pour laquelle j'aimerais le savoir est que je puisse déterminer par programme si je veux sortir la valeur à l'aide de .ToString("N0")ou .ToString("N2"). S'il n'y a pas de valeur décimale, je ne veux pas le montrer.

Réponses:


410

Pour les nombres à virgule flottante, n % 1 == 0c'est généralement le moyen de vérifier s'il y a quelque chose après le point décimal.

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

Production:

False
True

Mise à jour: Comme @Adrian Lopez l'a mentionné ci-dessous, la comparaison avec une petite valeurepsilonéliminera les erreurs de calcul en virgule flottante. Étant donné que la question concerne lesdoublevaleurs, voici uneréponse à l' épreuve des calculs en virgule flottante :

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

96
Cela fonctionne lorsque le nombre commence comme un nombre entier, mais pas nécessairement lorsque le nombre est le résultat d'un calcul à virgule flottante. Que diriez-vous de quelque chose comme "(d% 1) <epsilon" où epsion est une petite valeur?
Adrian Lopez

9
Il est dommage que la meilleure réponse dans ce fil soit un commentaire, plutôt que la réponse acceptée. Sympa Adrian.
starskythehutch

13
Je pense également que le commentaire d'Adrian ci-dessus est la meilleure réponse. Pour mettre son conseil en code C # formel: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Faites quelque chose si n est entier}.
Ruben Ramirez Padron

7
À mon humble avis, l'opérateur de module et les nombres à virgule flottante ne sont tout simplement pas utiles. Le code suggéré est incroyablement déroutant compte tenu du nombre de frappes utilisées et ne fonctionnera presque nulle part en dehors des langages .NET. Je parierais que c'est aussi beaucoup plus lent que la bonne façon (qui n'utilise aucune division). La bonne façon de procéder est d'utiliser Math.Abs(d-(int)d) < double.Epsilon. Comme nous aurions tous dû apprendre au cours de la première semaine de notre premier cours de programmation au collège.
krowe2

9
En fait, comme la question est posée, cette réponse est correcte et les commentaires sont erronés. L'OP ne veut pas savoir si un double est un entier à des fins mathématiques, mais plutôt comment l'afficher. Seules les valeurs entières exactes doivent être affichées sans point décimal. De plus, le commentaire sur le fait que le mod n'est pas utile avec la virgule flottante et ne fonctionne pas en dehors de .NET n'est pas bien informé. Et (int)dc'est une catastrophe qui lèvera une exception pour la plupart des valeurs doubles.
Jim Balter

49

Il existe plusieurs façons de procéder. Par exemple:

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

Vous pouvez également utiliser modulo.

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

L'un d'eux serait-il plus rapide que l'autre? Je veux le faire dans un contexte sensible aux performances.
Basil

@Basil - Dépend des circonstances. Vous devriez faire quelques synchronisations pour vous-même et juger.
Erik Funkenbusch

4
Math.Abs(d-(int)d) < double.Epsilonest plus sûr qued == (int)d
Reactgular

3
@MathewFoscarini - Je pense que vous êtes confus. Il la définit sur false, car le résultat de 16.1 - 6.1 n'est pas un entier. Il s'agissait de trouver si une valeur donnée est un int, pas si quelque chose qui est approximativement un int est un int.
Erik Funkenbusch

1
@MathewFoscarini - Oui, un int est un nombre sans valeur décimale (ou une valeur décimale de 0). 16.1-6.1 ne crée pas une valeur décimale 0, c'est une très petite valeur non nulle qui est causée par des bizarreries au format virgule flottante IEEE. Il n'y a aucun moyen de savoir si le nombre est censé avoir une valeur décimale ou non, donc supposer une valeur arrondie est tout aussi inexact. Le but de la question était de savoir si un nombre à virgule flottante était un entier, et non s'il s'agissait approximativement d'un entier.
Erik Funkenbusch

21

Que dis-tu de ça?

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

Même code pour decimal.

Mark Byers a fait un bon argument, en fait: ce n'est peut-être pas ce que vous voulez vraiment . Si vous vous souciez vraiment de savoir si un nombre arrondi aux deux décimales les plus proches est un entier , vous pouvez le faire à la place:

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

1
peut-être mettre à jour votre solution et ajouter: && numéro <int.MaxValue && numéro> int.MinValue
Walter Vehoeven

12

Alors que les solutions proposées semblent fonctionner pour des exemples simples, faire cela en général est une mauvaise idée. Un nombre peut ne pas être exactement un entier, mais lorsque vous essayez de le formater, il est suffisamment proche d'un entier que vous obtenez 1.000000. Cela peut se produire si vous effectuez un calcul qui, en théorie, devrait donner exactement 1, mais en pratique, donne un nombre très proche mais pas exactement égal à un en raison d'erreurs d'arrondi.

Au lieu de cela, formatez-le d'abord et si votre chaîne se termine par un point suivi de zéros, puis supprimez-les. Il existe également certains formats que vous pouvez utiliser pour supprimer automatiquement les zéros de fin. Cela pourrait être suffisant pour votre objectif.

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

Production:

1
1.02

@Mark Semble intéressant. Avez-vous un exemple de format qui supprime les zéros de fin?
Jim Geurts

Je suis d'accord que c'est plus sûr et ce que l'OP devrait probablement faire, mais ce n'est pas une réponse à la question plus étroite (mais plus intéressante) de savoir si une valeur a une partie fractionnaire ou non.
Clifford

3
@Clifford: J'essaie généralement de répondre en fonction de ce qui est le mieux pour résoudre le problème des PO, pas en fonction de ce que dit le titre. Les titres sont rarement une description précise du problème.
Mark Byers

+1 Convenez qu'essayer de tester des flotteurs ou des doubles pour voir s'ils peuvent être des pouces est mauvais en raison des erreurs d'arrondi et de précision.
Romain Hippeau

1
Pour l'utilisation de l'argent, vous voudriez probablement que 1.2 soit affiché comme 1.20, ce qui n'est pas le cas avec la solution suggérée. N'importe quels preneurs?
Kjell Rilbe

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

Problemo solvo.

Edit: Pwned par Mark Rushakoff.


4
ou tout simplementreturn ceil(num) == num && floor(num) == num;
Brian Rasmussen

13
ou tout simplementreturn ceil(num) == floor(num);
gregsdennis

4

La réponse de Mark Rushakoff peut être plus simple, mais les éléments suivants fonctionnent également et peuvent être plus efficaces car il n'y a pas d'opération de division implicite:

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

et

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

Si vous voulez une seule expression pour les deux types, peut-être

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

4

Si limite supérieure et inférieure des Int32matières:

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

Premier test, puis lancé comme ceci, il lèverait une exception autre que retourner faux, peut-être mettre à jour votre réponse
Walter Vehoeven

@ordinateur, oui bon point. En ce qui concerne le lancement sur le casting, je suppose que cela dépendra de la configuration de votre projet.
nawfal

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

2

Vous pouvez utiliser la mise en forme de chaîne pour le type double. Voici un exemple:

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"

Faites-moi savoir si cela n'aide pas.


1
Cela ne répond pas vraiment à la question de déterminer si une valeur n'a pas de partie fractionnaire, ce qui est une question mathématique. C'est cependant probablement ce dont le PO a besoin compte tenu de sa note explicative.
Clifford

2
Oui, il veut juste formater une valeur double ou décimale sans point décimal. Merci ...
BALKANGraph


0

J'ai fait face à une situation similaire, mais où la valeur est une chaîne. L'utilisateur saisit une valeur qui est censée être un montant en dollars, je veux donc valider qu'il est numérique et comporte au plus deux décimales.

Voici mon code pour retourner vrai si la chaîne "s" représente un numérique avec au plus deux décimales et faux sinon. Il évite tout problème qui résulterait de l'imprécision des valeurs à virgule flottante.

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;
}

J'en discute plus en détail sur http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Cela suppose que vous travaillez avec une seule culture. Par exemple, cela ne fonctionnerait pas correctement avec des cultures qui représentent des décimales comme 1.000,00
Jim Geurts

0

L'utilisation de int.TryParse donnera ces résultats:

        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

Ce n'est peut-être pas la solution la plus élégante mais ça marche si vous n'êtes pas trop pointilleux!

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

8
Ceci est une solution terrible
Ospho

-2

Vous pouvez utiliser la méthode «TryParse».

int.TryParse()

Cela vérifie si la valeur peut être convertie en une valeur entière entière. Le résultat peut alors indiquer un indicateur qui peut être utilisé ailleurs dans votre code.


L'argument pour int.TryParse est une chaîne, pas un double.
Jim Balter

yourDouble.toString("G17")
BackDoorNoBaby

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.