Pourquoi la machine numeric_limits Epsilon ne satisfait pas la condition 1 + e> 1?
Si je ne me trompe pas, la définition de la machine Epsilon est le nombre le plus bas qui satisfait à la condition:
J'essayais de tester cela en utilisant le std::numeric_limits<float>::epsilon()
mais la valeur ne satisfait pas cela, si vous essayez d'obtenir le nombre flottant précédent avec std::nextafter
:
#include <cmath>
#include <iostream>
#include <limits>
int main() {
float e = std::numeric_limits<float>::epsilon();
float previous = std::nextafter(e, -std::numeric_limits<float>::infinity());
std::cout << std::boolalpha << ((1.0f + previous) > 1.0f) << std::endl;
return 0;
}
Cela sort toujours true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
Après avoir essayé d'obtenir le numéro en utilisant, std::nextafter
j'ai remarqué que la bonne machine Epsilon devrait être:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
Je l'ai testé en utilisant ce code:
#include <cmath>
#include <iostream>
#include <limits>
bool verify(float e) {
return ((1.0f + e) > 1.0f);
}
int main() {
std::cout.precision(std::numeric_limits<float>::digits);
std::cout << std::boolalpha << std::fixed;
float epsilon = std::numeric_limits<float>::epsilon();
float last = epsilon;
while (true) {
last = std::nextafter(last, -std::numeric_limits<float>::infinity());
if ((1.0f + last) > 1.0f) {
epsilon = last;
} else {
break;
}
}
// Does not satisfy condition
std::cout << "last: " << verify(last) << " " << last << std::endl;
// Satisfy condition
std::cout << "epsilon: " << verify(epsilon) << " " << epsilon << std::endl;
float half_epsilon = std::numeric_limits<float>::epsilon() / 2.0f;
float actual_epsilon = std::nextafter(half_epsilon, std::numeric_limits<float>::infinity());
// Same as 'last' at this point
std::cout << "half_epsilon: " << verify(half_epsilon) << " " << half_epsilon << std::endl;
// Same as 'epsilon' at this point
std::cout << "actual_epsilon: " << verify(actual_epsilon) << " " << actual_epsilon << std::endl;
return 0;
}
Cette sortie
last: false 0.000000059604644775390625
epsilon: true 0.000000059604651880817983
half_epsilon: false 0.000000059604644775390625
actual_epsilon: true 0.000000059604651880817983
https://coliru.stacked-crooked.com/a/3c66a2144e80a91b
Est-ce que je manque quelque chose ici?
Réponses
Si je ne me trompe pas, la définition de la machine Epsilon est le plus petit nombre qui satisfait à la condition: [
1 + epsilon > 1
]
Fermez, mais vous vous trompez dans le contexte de C ++. (Je pense que votre définition est correcte dans d'autres contextes plus académiques.) Selon cppreference.com , la machine epsilon est "la différence entre 1.0
et la valeur suivante représentable par le type à virgule flottante [spécifié]". La machine epsilon satisfait 1 + epsilon > 1
, mais il n'est pas nécessaire que ce soit le nombre le plus bas qui satisfait cela. C'est, cependant, le nombre le plus bas qui satisfait cette condition dans tous les modes d'arrondi .
Parce que la machine epsilon est tellement plus petite que 1.0
, il y a beaucoup de valeurs représentables entre epsilon et 0.0
. (C'est un objectif de base des représentations en virgule flottante.) Lorsque l'un de ces éléments est ajouté 1.0
, le résultat n'est pas représentable, le résultat doit donc être arrondi. Si le mode d'arrondi est à la valeur représentable la plus proche, cette somme sera arrondie à 1 + epsilon
chaque fois que le petit nombre est compris entre epsilon/2
et 3*epsilon/2
. En revanche, si le mode d'arrondi est toujours vers zéro, alors vous obtenez le résultat que vous attendiez.
Essayez d'ajouter #include <cfenv>
et la ligne suivante à votre code.
fesetround(FE_TOWARDZERO);
Cela provoque l' arrondi de toute somme strictement comprise entre 1.0
et . Vous devriez maintenant voir la machine epsilon se comporter comme prévu.1 + epsilon
1.0
Les autres modes d'arrondi garantis sont vers -infini et vers + infini. Voir cppreference.com pour plus de détails.