web-dev-qa-db-fra.com

Vérifier si float est un entier

Comment puis-je vérifier si une variable 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 beaucoup).

65
sidyll

Outre les réponses précises déjà données, vous pouvez également utiliser ceilf(f) == f ou floorf(f) == f. Les deux expressions renvoient true si f est un entier. Ils renvoient également false pour NaNs ( les NaN comparent toujours inégales ) et true pour ± infini, et ne rencontrent pas le problème du dépassement du type entier utilisé pour contenir le résultat tronqué, parce que floorf()/ceilf() renvoie floats.

66
Marc Mutz - mmutz

Gardez à l'esprit que la plupart des techniques présentées ici sont valides, en supposant que l'erreur d'arrondi due à des calculs antérieurs n'est pas un facteur. Par exemple. vous pourriez 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 (comme ceilf, transtyper en long, etc.) est que, bien qu’elles fonctionnent parfaitement pour les constantes de nombres entiers, elles échoueront si le nombre est un. résultat d’un calcul 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 (31/20)20 devrait être égal à 3, car le résultat réel du calcul a finalement été 2.9999992847442626953125 .

Toute méthode similaire, que ce soit fmodf ou autre, est sujette à cela. Dans les applications qui effectuent des calculs complexes ou sujets à l'arrondi, vous souhaitez généralement définir une valeur de "tolérance" pour ce qui constitue un "nombre entier" (ceci s'applique aux comparaisons d'égalité à virgule flottante en général). Nous appelons souvent cette tolérance epsilon . Par exemple, supposons que l'ordinateur pardonne jusqu'à +/- 0,00001 erreur d'arrondi. Ensuite, 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 parce que par exemple. ceilf(1.0000001) est 2 pas 1, et ceilf(-1.99999999) est -1 pas -2.

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

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

18
Jason C

stdlib float modf (float x, float * ipart) se scinde en deux parties, vérifie si la valeur renvoyée (partie décimale) == 0.

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

N'oublie pas math.h et libm.

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

Je ne suis pas sûr à 100%, mais lorsque vous transformez f en int et que vous le soustrayez de f, je pense qu'il est converti en float. Cela n’aura probablement pas d’importance dans ce cas, mais cela pourrait poser des problèmes si vous vous attendez à ce que ce soit un int pour une raison quelconque.

Je ne sais pas si c'est une meilleure solution en soi, mais vous pouvez utiliser le module mathématique à la place, par exemple: float f = 4.5886; bool isInt; isInt = (f % 1.0 != 0) ? false : true;, en fonction de votre compilateur, vous pouvez ou non avoir besoin du .0 après le 1, encore une fois. la chose implicite jette en jeu. Dans ce code, la valeur bool isInt doit être vraie si le droit du séparateur décimal est égal à zéro, et faux sinon.

0
Controllerface
#define twop22 (0x1.0p+22)
#define ABS(x) (fabs(x))
#define isFloatInteger(x) ((ABS(x) >= twop22) || (((ABS(x) + twop22) - twop22) == ABS(x)))
0
kanna