Aujourd'hui, je regardais du code C ++ (écrit par quelqu'un d'autre) et j'ai trouvé cette section:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
J'essaie de comprendre si cela a du sens.
La documentation de epsilon()
dit:
La fonction renvoie la différence entre 1 et la plus petite valeur supérieure à 1 qui est représentable [par un double].
Cela s'applique-t-il également à 0, c'est epsilon()
-à- dire que la plus petite valeur est supérieure à 0? Ou y a-t-il des nombres entre 0
et 0 + epsilon
qui peuvent être représentés par un double
?
Sinon, la comparaison n'est-elle pas équivalente someValue == 0.0
?
numeric_limits<>::epsilon
est trompeuse et non pertinente. Ce que nous voulons, c'est supposer 0 si la valeur réelle ne diffère pas de plus de quelques ε de 0. Et ε doit être choisi en fonction de la spécification du problème, et non en fonction d'une valeur dépendante de la machine. Je soupçonne que l'epsilon actuel est inutile, car même quelques opérations de FP peuvent accumuler une erreur supérieure à cela.Réponses:
En supposant un double IEEE 64 bits, il y a une mantisse 52 bits et un exposant 11 bits. Brisons-le en morceaux:
Le plus petit nombre représentable supérieur à 1:
Par conséquent:
Y a-t-il des nombres entre 0 et epsilon? Beaucoup ... Par exemple, le nombre minimal représentable positif (normal) est:
En fait, il y a des
(1022 - 52 + 1)×2^52 = 4372995238176751616
nombres entre 0 et epsilon, ce qui représente 47% de tous les nombres positifs représentables ...la source
0 <= e < 2048
alors la mantisse est multipliée par 2 à la puissance dee - 1023
. Par exemple, l'exposant de2^0
est codé commee=1023
,2^1
commee=1024
et2^-1022
commee=1
. La valeur dee=0
est réservée aux sous-normales et au zéro réel.2^-1022
est également le plus petit nombre normal . Le plus petit nombre est en fait0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074
. Ceci est subnormal, ce qui signifie que la partie mantisse est inférieure à 1, elle est donc codée avec l'exposante=0
.Le test n'est certainement pas le même que
someValue == 0
. L'idée globale des nombres à virgule flottante est qu'ils stockent un exposant et une signification. Ils représentent donc une valeur avec un certain nombre de chiffres binaires significatifs de précision (53 dans le cas d'un double IEEE). Les valeurs représentables sont beaucoup plus densément emballées près de 0 que près de 1.Pour utiliser un système décimal plus familier, supposons que vous stockiez une valeur décimale "à 4 chiffres significatifs" avec exposant. Ensuite , la valeur suivante représentable supérieure
1
est1.001 * 10^0
etepsilon
est1.000 * 10^-3
. Mais1.000 * 10^-4
est également représentable, en supposant que l'exposant peut stocker -4. Vous pouvez me croire sur parole qu'un double IEEE peut stocker moins d'exposants que l'exposant deepsilon
.Vous ne pouvez pas dire à partir de ce code seul s'il est logique ou non d'utiliser
epsilon
spécifiquement comme limite, vous devez regarder le contexte. Il se peut que ceepsilon
soit une estimation raisonnable de l'erreur dans le calcul qui a produitsomeValue
, et il se peut que ce ne soit pas le cas.la source
someValue == 0.0
ou non.Il existe des nombres qui existent entre 0 et epsilon car epsilon est la différence entre 1 et le nombre le plus élevé suivant qui peut être représenté au-dessus de 1 et non la différence entre 0 et le nombre le plus élevé suivant qui peut être représenté au-dessus de 0 (si c'était le cas, cela le code ferait très peu): -
À l'aide d'un débogueur, arrêtez le programme à la fin de main et regardez les résultats et vous verrez que epsilon / 2 est différent de epsilon, zéro et un.
Cette fonction prend donc des valeurs entre +/- epsilon et les met à zéro.
la source
Une approximation d'epsilon (plus petite différence possible) autour d'un nombre (1.0, 0.0, ...) peut être imprimée avec le programme suivant. Il imprime la sortie suivante:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
Un peu de réflexion montre clairement que l'epsilon devient plus petit plus le nombre est petit que nous utilisons pour regarder sa valeur epsilon, parce que l'exposant peut s'ajuster à la taille de ce nombre.
la source
Supposons que nous travaillons avec des nombres à virgule flottante jouets qui tiennent dans un registre de 16 bits. Il y a un bit de signe, un exposant de 5 bits et une mantisse de 10 bits.
La valeur de ce nombre à virgule flottante est la mantisse, interprétée comme une valeur décimale binaire, multipliée par deux à la puissance de l'exposant.
Autour de 1, l'exposant est égal à zéro. Donc, le plus petit chiffre de la mantisse est une partie en 1024.
Près de la moitié de l'exposant est moins un, donc la plus petite partie de la mantisse est deux fois moins grande. Avec un exposant de cinq bits, il peut atteindre un négatif de 16, auquel cas la plus petite partie de la mantisse vaut une partie sur 32 m. Et à un exposant négatif de 16, la valeur est d'environ une partie en 32k, beaucoup plus proche de zéro que l'epsilon autour de celui que nous avons calculé ci-dessus!
Il s'agit maintenant d'un modèle en virgule flottante jouet qui ne reflète pas toutes les bizarreries d'un véritable système à virgule flottante, mais la capacité de refléter des valeurs plus petites que epsilon est raisonnablement similaire avec des valeurs en virgule flottante réelles.
la source
La différence entre
X
et la valeur suivante deX
varie selonX
.epsilon()
n'est que la différence entre1
et la valeur suivante de1
.La différence entre
0
et la valeur suivante de0
n'est pasepsilon()
.Au lieu de cela, vous pouvez utiliser
std::nextafter
pour comparer une valeur double avec0
comme suit:la source
Je pense que cela dépend de la précision de votre ordinateur. Jetez un oeil sur ce tableau : vous pouvez voir que si votre epsilon est représenté par un double, mais que votre précision est plus élevée, la comparaison n'est pas équivalente à
Bonne question quand même!
la source
Vous ne pouvez pas appliquer cela à 0, à cause de la mantisse et des parties exposantes. En raison de l'exposant, vous pouvez stocker de très petits nombres, qui sont plus petits que epsilon, mais lorsque vous essayez de faire quelque chose comme (1.0 - "très petit nombre"), vous obtenez 1.0. Epsilon est un indicateur non pas de valeur, mais de précision de valeur, qui est en mantisse. Il montre combien de chiffres décimaux conséquents corrects de nombre nous pouvons stocker.
la source
Avec la virgule flottante IEEE, entre la plus petite valeur positive non nulle et la plus petite valeur négative non nulle, il existe deux valeurs: zéro positif et zéro négatif. Tester si une valeur se situe entre les plus petites valeurs non nulles équivaut à tester l'égalité avec zéro; l'affectation, cependant, peut avoir un effet, car elle changerait un zéro négatif en un zéro positif.
Il serait concevable qu'un format à virgule flottante puisse avoir trois valeurs entre les plus petites valeurs positives et négatives finies: infinitésimal positif, zéro non signé et infinitésimal négatif. Je ne connais aucun format à virgule flottante qui fonctionne en fait de cette façon, mais un tel comportement serait parfaitement raisonnable et sans doute meilleur que celui de l'IEEE (peut-être pas assez mieux pour valoir la peine d'ajouter du matériel supplémentaire pour le prendre en charge, mais mathématiquement 1 / (1 / INF), 1 / (- 1 / INF) et 1 / (1-1) devraient représenter trois cas distincts illustrant trois zéros différents). Je ne sais pas si une norme C exigerait que les infinitésimaux signés, s'ils existent, devraient se comparer à zéro. Si ce n'est pas le cas, un code comme celui ci-dessus pourrait utilement garantir que, par exemple,
la source
Supposons donc que le système ne puisse pas distinguer 1.000000000000000000000 et 1.000000000000000000001. c'est-à-dire 1.0 et 1.0 + 1e-20. Pensez-vous qu'il existe encore des valeurs pouvant être représentées entre -1e-20 et + 1e-20?
la source
epsilon
. Parce que c'est une virgule flottante , pas une virgule fixe.De plus, une bonne raison d'avoir une telle fonction est de supprimer les "dénormals" (ces très petits nombres qui ne peuvent plus utiliser le "1" implicite et qui ont une représentation FP spéciale). Pourquoi voudriez-vous faire ça? Parce que certaines machines (en particulier certains Pentium 4 plus anciens) deviennent vraiment, vraiment lentes lors du traitement des dénormals. D'autres deviennent un peu plus lents. Si votre application n'a pas vraiment besoin de ces très petits nombres, les vider à zéro est une bonne solution. Les bons endroits à considérer sont les dernières étapes de tout filtre IIR ou fonction de désintégration.
Voir aussi: Pourquoi le passage de 0,1f à 0 ralentit-il 10 fois les performances?
et http://en.wikipedia.org/wiki/Denormal_number
la source