82 votes

Vérifier si un flottant est un entier

Comment puis-je vérifier si un float contient une valeur entière ? Jusqu'à présent, j'ai utilisé :

float f = 4.5886;
if (f-(int)f == 0)
     printf("yes\n");
else printf("no\n");

Mais je me demande s'il existe une meilleure solution, ou si celle-ci présente des inconvénients (ou de nombreux inconvénients).

9 votes

Votre méthode échoue lorsque le nombre est supérieur à la valeur entière maximale autorisée.

0 votes

Voir ma réponse pour une solution au problème de l'approche de l'OP.

0 votes

La réponse correcte est certainement : vous posez la mauvaise question.

79voto

Marc Mutz - mmutz Points 10367

En dehors des bonnes réponses déjà données, vous pouvez également utiliser ceilf(f) == f ou floorf(f) == f . Les deux expressions renvoient true si f est un nombre entier. Ils renvoient également false pour les NaNs ( Les NaNs comparent toujours des valeurs inégales ) et true pour ±infini, et n'ont pas le problème de déborder le type entier utilisé pour contenir le résultat tronqué, car floorf() / ceilf() retourner float s.

1 votes

Eh bien, ça va à l'encontre du but recherché. Ce que je veux, c'est vérifier si le flottant est un entier, comme le dit le titre ; et non l'arrondir. Dans ce dernier cas, j'aurais toujours un entier. Mais merci de me l'avoir fait remarquer.

1 votes

@sidyll : les deux expressions sont rondes f oui, mais ils le font dans le but de vérifier si c'est un nombre entier.

0 votes

@sidyll : plus précisément : notez comment le résultat de l'opération d'arrondi est à son tour comparé à f J'ai modifié ma réponse pour être plus clair sur ce point, merci.

28voto

Jason C Points 14927

Gardez à l'esprit que la plupart des techniques présentées ici sont valables en supposant que l'erreur d'arrondi due aux calculs antérieurs n'est pas un facteur. Par exemple, vous pourrait utiliser roundf comme ceci :

float z = 1.0f;

if (roundf(z) == z) {
    printf("integer\n");
} else {
    printf("fraction\n");
}

Le problème avec cette technique et d'autres techniques similaires (telles que ceilf , le casting à long ) est que, bien qu'ils fonctionnent parfaitement pour les constantes de nombres entiers, ils échouent si le nombre est le résultat d'un calcul qui a été sujet à une erreur d'arrondi en virgule flottante. Par exemple :

float z = powf(powf(3.0f, 0.05f), 20.0f);

if (roundf(z) == z) {
    printf("integer\n");
} else {
    printf("fraction\n");
}

Imprime "fraction", même si (3 1/20 ) 20 devrait être égal à 3, car le résultat réel du calcul a fini par être 2.9999992847442626953125 .

Toute méthode similaire, qu'elle soit fmodf ou autre, est soumis à cela. Dans les applications qui effectuent des calculs complexes ou sujets aux arrondis, on souhaite généralement définir une certaine valeur de "tolérance" pour ce qui constitue un "nombre entier" (cela vaut pour les comparaisons d'égalité en virgule flottante en général). Nous appelons souvent cette tolérance epsilon . Par exemple, disons que nous pardonnons à l'ordinateur jusqu'à +/- 0,00001 d'erreur d'arrondi. Alors, si nous testons z nous pouvons choisir un epsilon de 0.00001 et faire :

if (fabsf(roundf(z) - z) <= 0.00001f) {
    printf("integer\n");
} else {
    printf("fraction\n");
}

Vous ne voulez pas vraiment utiliser ceilf ici, car par exemple ceilf(1.0000001) est 2 et non 1, et ceilf(-1.99999999) est -1 et non -2.

Vous pourriez utiliser rintf à la place de roundf si vous préférez.

Choisissez une valeur de tolérance adaptée à votre application (et oui, parfois la tolérance zéro est appropriée). Pour plus d'informations, consultez cet article sur comparaison de nombres à virgule flottante .

11voto

if (fmod(f, 1) == 0.0) {
  ...
}

N'oubliez pas math.h et libm .

3 votes

Cela fonctionne, mais en principe fmod est une opération assez coûteuse.

9voto

DavidNeiss Points 3294

Stdlib float modf (float x, float *ipart) divise en deux parties, vérifie si la valeur de retour (partie fractionnaire) == 0.

0 votes

Je continue à me demander si ce n'est pas trop cher, si on compare avec ma solution initiale (comme le dit @R dans l'autre réponse similaire).

2 votes

@mmutz : ai-je dit que je devais utiliser des lunettes ?

0 votes

Cela fonctionne-t-il lorsque le nombre est 23.9999999999 ? il me semble que cela ne fonctionne que lorsque le nombre est 24.0000000000001

7voto

R.. Points 93718
if (f <= LONG_MIN || f >= LONG_MAX || f == (long)f) /* it's an integer */

1 votes

Si sizeof(f) > sizeof(long) cela pourrait ne pas tenir.

0 votes

long doit être au moins de 32 bits. La norme C impose aucune exigence sur la qualité des types à virgule flottante, mais en réalité float sera toujours soit une simple précision IEEE, soit quelque chose de pire, avec moins de 32 bits dans la mantisse.

2 votes

Je fais juste une remarque au cas où quelqu'un trouverait cette réponse et essaierait de l'appliquer à une double .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X