Tout d'abord, les valeurs à virgule flottante ne sont pas "aléatoire" dans leur comportement. Exact, la comparaison et n'a de sens que dans beaucoup d'usages. Mais si vous allez utiliser de virgule flottante, vous devez être conscient de la façon dont il fonctionne. Axées sur la supposant à virgule flottante fonctionne comme les nombres réels, vous obtiendrez un code qui rompt rapidement. Axées sur la supposant à virgule flottante résultats ont de grandes aléatoire fuzz qui y sont associés (comme la plupart des réponses suggèrent ici) vous obtiendrez le code qui s'affiche à travailler au début, mais finit par avoir de grande ampleur des erreurs et brisé cas du coin.
Tout d'abord, si vous souhaitez programmer avec virgule flottante, vous devriez lire ceci:
Ce Que Tout Informaticien Devez Savoir À Propos De L'Arithmétique À Virgule Flottante
Oui, lire tout ça. Si c'est un fardeau trop lourd, vous devez utiliser des entiers/point fixe pour vos calculs jusqu'à ce que vous ayez le temps de le lire. :-)
Maintenant, avec cela dit, le plus gros soucis avec l'exacte virgule flottante comparaisons descendre à:
Le fait que beaucoup de valeurs que vous pouvez écrire dans la source, ou de lire scanf
ou strtod
, n'existent pas en tant que valeurs à virgule flottante et obtenir silencieusement converti à la plus proche approximation. C'est ce que demon9733 réponse était en train de parler.
Le fait que de nombreux résultats sont arrondis à cause de ne pas avoir assez de précision pour représenter le résultat réel. Un exemple simple où vous pouvez le voir c'est l'ajout d' x = 0x1fffffe
et y = 1
flotteurs. Ici, x
a 24 bits de précision dans la mantisse (ok) et y
a juste 1 peu, mais quand vous les ajoutez, de leurs morceaux ne sont pas dans le chevauchement des lieux, et le résultat aurait besoin de 25 bits de précision. Au lieu de cela, il devient arrondi (à 0x2000000 " dans le mode d'arrondi par défaut).
Le fait que de nombreux résultats sont arrondis à cause de besoin d'un nombre infini de lieux pour la valeur correcte. Cela comprend à la fois des résultats rationnels comme 1/3 (que vous êtes familier avec de virgule où il prend une infinité de lieux) mais aussi 1/10 (qui prend également en une infinité de lieux en binaire, depuis le 5 n'est pas une puissance de 2), ainsi que l'irrationnel résultats comme la racine carrée de tout ce qui n'est pas un carré parfait.
Double arrondi. Sur certains systèmes (en particulier x86), floating point les expressions sont évaluées dans une précision plus élevée que la valeur nominale des types. Cela signifie que lorsque l'un des types ci-dessus de l'arrondi se produit, vous bénéficiez de deux arrondissement étapes, d'abord une approximation du résultat d'une plus grande précision du type, puis un arrondi pour le type final. Par exemple, considérons ce qui se passe en décimal si vous tour de 1,49 à un nombre entier (1), par rapport à ce qui se passe si vous arrondir à la décimale près (1.5) ronde alors que la résultat à un nombre entier (2). C'est en fait l'une des pires zones à traiter en virgule flottante, depuis le comportement du compilateur (surtout pour le buggy, la non-conforme compilateurs comme GCC) est imprévisible.
Les fonctions transcendantes (trig, exp, log, etc.) ne sont pas spécifiés correctement arrondie résultats; le résultat est tout simplement de corriger dans un délai d'une unité à la dernière place de la précision (généralement appelé 1ulp).
Lorsque vous écrivez du code de nombres flottants, vous devez garder à l'esprit ce que vous faites avec les numéros qui pourraient entraîner que les résultats soient inexactes, et de faire des comparaisons en conséquence. Souvent, il va donner un sens à comparer avec un "epsilon", mais que epsilon devraient être basées sur l' ampleur de l'un des numéros de la comparaison, non pas absolue constante. (Dans les cas où une absolue constante epsilon, c'est très indicatifs ce point fixe, pas de virgule flottante, c'est le bon outil pour le travail!)
Edit: En particulier, l'ampleur relative de epsilon devrait ressembler à quelque chose comme:
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))
Où FLT_EPSILON
est la constante de float.h
(remplacer par DBL_EPSILON
pour les doubles ou LDBL_EPSILON
pour les long double) et K
est une constante vous de choisir de tels que l'intégration de l'erreur de calcul est définitivement délimitée par K
les unités dans la dernière place (et si vous n'êtes pas sûr que vous avez l'erreur liée calcul de la droite, font K
quelques fois plus grand que ce que vos calculs dire qu'il devrait être).
Enfin, notez que si vous utilisez cette, certains soins spéciaux peuvent être nécessaires proche de zéro, depuis FLT_EPSILON
n'a pas de sens pour denormals. Une solution rapide serait de faire:
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)
et de même substitut DBL_MIN
si l'utilisation de doubles.