Le code suivant est évidemment faux. Quel est le problème?
i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15
r
floating-point
floating-accuracy
r-faq
dplanet
la source
la source
Réponses:
Raison générale (indépendante de la langue)
Étant donné que tous les nombres ne peuvent pas être représentés exactement dans l' arithmétique à virgule flottante IEEE (la norme que presque tous les ordinateurs utilisent pour représenter les nombres décimaux et faire des calculs avec eux), vous n'obtiendrez pas toujours ce que vous attendiez. Cela est particulièrement vrai car certaines valeurs qui sont des décimales simples et finies (telles que 0,1 et 0,05) ne sont pas représentées exactement dans l'ordinateur et les résultats de l'arithmétique peuvent donc ne pas donner un résultat identique à une représentation directe du " réponse connue.
Il s'agit d'une limitation bien connue de l'arithmétique informatique et est discutée à plusieurs endroits:
Comparaison des scalaires
La solution standard à cela
R
n'est pas d'utiliser==
, mais plutôt laall.equal
fonction. Ou plutôt, puisqueall.equal
donne beaucoup de détails sur les différences s'il y en a ,isTRUE(all.equal(...))
.les rendements
Quelques autres exemples d'utilisation
all.equal
au lieu de==
(le dernier exemple est censé montrer que cela montrera correctement les différences).Quelques détails supplémentaires, directement copiés d'une réponse à une question similaire :
Le problème que vous avez rencontré est que la virgule flottante ne peut pas représenter exactement les fractions décimales dans la plupart des cas, ce qui signifie que vous constaterez fréquemment que les correspondances exactes échouent.
tandis que R se trouve légèrement lorsque vous dites:
Vous pouvez découvrir ce qu'il pense vraiment en décimal:
Vous pouvez voir que ces chiffres sont différents, mais la représentation est un peu lourde. Si nous les regardons en binaire (enfin, hex, ce qui est équivalent), nous obtenons une image plus claire:
Vous pouvez voir qu'ils diffèrent par
2^-53
, ce qui est important car ce nombre est la plus petite différence représentable entre deux nombres dont la valeur est proche de 1, comme c'est le cas.Nous pouvons découvrir pour n'importe quel ordinateur donné ce que ce plus petit nombre représentable est en regardant dans le champ machine de R :
Vous pouvez utiliser ce fait pour créer une fonction «presque égale» qui vérifie que la différence est proche du plus petit nombre représentable en virgule flottante. En fait cela existe déjà:
all.equal
.Ainsi, la fonction all.equal vérifie en fait que la différence entre les nombres est la racine carrée de la plus petite différence entre deux mantisses.
Cet algorithme devient un peu drôle près de très petits nombres appelés dénormals, mais vous n'avez pas à vous en soucier.
Comparaison des vecteurs
La discussion ci-dessus suppose une comparaison de deux valeurs uniques. Dans R, il n'y a pas de scalaires, juste des vecteurs et la vectorisation implicite est une force du langage. Pour comparer la valeur des vecteurs par élément, les principes précédents sont valables, mais la mise en œuvre est légèrement différente.
==
est vectorisé (fait une comparaison élément par élément) tandis queall.equal
compare les vecteurs entiers comme une seule entité.Utilisation des exemples précédents
==
ne donne pas le résultat "attendu" etall.equal
ne fonctionne pas par élémentAu contraire, une version qui fait une boucle sur les deux vecteurs doit être utilisée
Si une version fonctionnelle de celle-ci est souhaitée, elle peut être écrite
qui peut être appelé juste
Alternativement, au lieu d'encapsuler
all.equal
encore plus d'appels de fonction, vous pouvez simplement répliquer les internes pertinentsall.equal.numeric
et utiliser la vectorisation implicite:Telle est l’approche adoptée par
dplyr::near
, qui sela source
Ajout au commentaire de Brian (qui est la raison), vous pouvez surmonter cela en utilisant à la
all.equal
place:Par l'avertissement de Joshua, voici le code mis à jour (merci Joshua):
la source
all.equal
ne revient pasFALSE
quand il y a des différences, vous devez donc envelopper avecisTRUE
lors de son utilisation dans uneif
instruction.C'est hackish, mais rapide:
la source
all.equal(... tolerance)
paramètre.all.equal(0.147, 0.15, tolerance=0.05)
est vrai.dplyr::near()
est une option pour tester si deux vecteurs de nombres à virgule flottante sont égaux. Voici l'exemple de la documentation :La fonction a un paramètre de tolérance intégré:
tol = .Machine$double.eps^0.5
qui peut être ajusté. Le paramètre par défaut est le même que celui par défaut pourall.equal()
.la source
J'avais un problème similaire. J'ai utilisé la solution suivante.
sortie d'intervalles de coupe inégaux en fonction des options (chiffres = 2):
sortie d'intervalles de coupe égaux basés sur la fonction ronde:
la source
Comparaisons généralisées ("<=", "> =", "=") en arithmétique à double précion:
Comparaison de a <= b:
Comparaison de a> = b:
Comparaison de a = b:
la source