Est-ce que « epsilon » garantit vraiment quoi que ce soit dans les calculs en virgule flottante?

StackOverflow https://stackoverflow.com/questions/2729637

Question

Pour rendre le problème à court disons que je veux calculer l'expression a / (b - c) sur floats.

Pour que le résultat est significatif, je peux vérifier si b et c sont égaux:

float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
    return a / (b - c);
}

mais mes tests montrent qu'il ne suffit pas de garantie soit des résultats significatifs, ni ne manquant pas de fournir un résultat s'il est possible.

Cas 1:

a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;

Résultat:. si la condition est pas remplie, mais l'expression produirait un résultat correct 100000008 (comme pour la précision des flotteurs)

Cas n ° 2:

a = 1e33f;
b = 0.000003;
c = 0.000002;

. Résultat: si la condition est remplie, mais l'expression ne produit pas un résultat significatif +1.#INF00

Je l'ai trouvé beaucoup plus fiable pour vérifier le résultat, pas les arguments:

const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
     return x;
}

Mais pour quoi est le epsilon alors et pourquoi tout le monde dit epsilon est bon d'utiliser?

Était-ce utile?

La solution

« vous devez utiliser un epsilon lorsqu'ils traitent avec des flotteurs » est une réaction réflexe des programmeurs avec une compréhension superficielle des calculs en virgule flottante, pour les comparaisons en général (non seulement à zéro).

Ceci est généralement inutile parce qu'il ne vous dit pas comment minimiser la propagation des erreurs d'arrondi, il ne vous dit pas comment éviter les problèmes d'annulation ou d'absorption, et même si votre problème est en effet lié à la comparaison de deux flotteurs, il ne vous dit pas ce que la valeur de epsilon est bon pour ce que vous faites .

Si vous avez pas lu Ce que tout informaticien devrait savoir a propos arithmétique à virgule flottante , il est un bon point de départ. De plus que cela, si vous êtes intéressé par la précision du résultat de la division dans votre exemple, vous devez estimer b-c imprécise a été faite par précédent erreurs d'arrondi, car en effet si b-c est petite, une petite erreur absolue correspond à une grande erreur absolue sur le résultat. Si votre préoccupation est seulement que la division ne doit pas déborder, votre test (le résultat) est juste. Il n'y a aucune raison de test pour un diviseur nul avec des nombres à virgule flottante, vous testez juste pour débordement du résultat, qui saisit à la fois les cas où le diviseur est nulle et où le diviseur est si petit à rendre le résultat non représentable avec précision.

En ce qui concerne la propagation des erreurs d'arrondi, il existe analyseurs spécialisés qui peuvent vous aider à estimer, car il est une chose pénible à faire à la main.

Autres conseils

Epsilon est utilisé pour déterminer si deux numéros soumis à l'arrondissement des chiffres sont assez proches pour être considéré comme « égal ». Notez qu'il est préférable de fabs(b/c - 1) < EPS de test que fabs(b-c) < EPS, et même mieux - grâce à la conception de flotteurs IEEE - à abs(*(int*)&b - *(int*)&c) < EPSI test (où EPSI est un petit nombre entier)

.

Votre problème est d'une autre nature, et probablement de test garantit le résultat plutôt que les entrées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top