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
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
Depuis pas tous les numéros peuvent être représentés exactement à virgule flottante IEEE de l'arithmétique (la norme de presque tous les ordinateurs utilisent pour représenter les nombres décimaux et de faire des maths avec eux), vous ne serez pas toujours obtenir ce que vous avez prévu. Cela est particulièrement vrai parce que certaines valeurs qui sont simples, fini de décimales (tels que 0,1 et 0,05) ne sont pas représentés exactement dans l'ordinateur, et donc les résultats de l'arithmétique ne peuvent donner un résultat identique à une représentation directe de la "connu" réponse.
C'est une limitation bien connue de l'arithmétique des ordinateurs et est discutée dans plusieurs endroits:
La solution standard pour ce en R
est de ne pas utiliser ==
, mais plutôt l' all.equal
fonction. Ou plutôt, depuis all.equal
donne beaucoup de détails sur les différences s'il y en a, isTRUE(all.equal(...))
.
if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")
les rendements
i equals 0.15
Quelques exemples de l'utilisation de all.equal
au lieu de ==
(le dernier exemple est censé montrer que cela permettra d'afficher correctement les différences).
> 0.1+0.05==0.15
[1] FALSE
> isTRUE(all.equal(0.1+0.05, 0.15))
[1] TRUE
> 1-0.1-0.1-0.1==0.7
[1] FALSE
> isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
[1] TRUE
> 0.3/0.1 == 3
[1] FALSE
> isTRUE(all.equal(0.3/0.1, 3))
[1] TRUE
> 0.1+0.1==0.15
[1] FALSE
> isTRUE(all.equal(0.1+0.1, 0.15))
[1] FALSE
Un peu plus en détail, directement copiées à partir d'une réponse à une question similaire:
Le problème que vous avez rencontré est qu'à virgule flottante ne peut pas représenter les fractions décimales exactement dans la plupart des cas, ce qui signifie que vous découvrirez souvent que les correspondances exactes échouer.
alors que R est légèrement lorsque vous dites:
> 1.1-0.2
[1] 0.9
> 0.9
[1] 0.9
Vous pouvez trouver ce qu'il pense vraiment qu'en décimal:
> sprintf("%.54f",1.1-0.2)
[1] "0.900000000000000133226762955018784850835800170898437500"
> sprintf("%.54f",0.9)
[1] "0.900000000000000022204460492503130808472633361816406250"
Vous pouvez voir ces numéros sont différents, mais la représentation est un peu lourd. Si nous les regardons en binaire (bien, hex, ce qui est équivalent), nous obtenons une image plus claire:
> sprintf("%a",0.9)
[1] "0x1.ccccccccccccdp-1"
> sprintf("%a",1.1-0.2)
[1] "0x1.ccccccccccccep-1"
> sprintf("%a",1.1-0.2-0.9)
[1] "0x1p-53"
Vous pouvez voir qu'ils diffèrent par 2^-53
, ce qui est important parce que ce nombre est représentable le plus faible de la différence entre deux nombres dont la valeur est proche de 1, comme c'est.
Nous pouvons trouver pour un ordinateur donné que ce plus petit nombre représentable est en regardant dans la R du champ machine:
> ?.Machine
....
double.eps the smallest positive floating-point number x
such that 1 + x != 1. It equals base^ulp.digits if either
base is 2 or rounding is 0; otherwise, it is
(base^ulp.digits) / 2. Normally 2.220446e-16.
....
> .Machine$double.eps
[1] 2.220446e-16
> sprintf("%a",.Machine$double.eps)
[1] "0x1p-52"
Vous pouvez utiliser ce fait pour créer une "presque égal à' la fonction qui vérifie que la différence est proche de représentable le plus faible nombre en virgule flottante. En fait, cela existe déjà: all.equal
.
> ?all.equal
....
all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality'.
....
all.equal(target, current,
tolerance = .Machine$double.eps ^ 0.5,
scale = NULL, check.attributes = TRUE, ...)
....
Donc le tout.l'égalité de la fonction est en fait de vérifier que la différence entre les nombres est la racine carrée de la plus petite différence entre deux mantissas.
Cet algorithme va un peu drôle à proximité de très petits nombres appelés denormals, mais vous n'avez pas besoin de s'inquiéter à ce sujet.
La discussion ci-dessus suppose une comparaison de deux valeurs. Dans R, il n'y a pas des scalaires, juste des vecteurs et implicites de la vectorisation est une force de la langue. Pour une comparaison de la valeur des vecteurs de l'élément de sage, les principes précédents, mais la mise en œuvre est légèrement différente. ==
est vectorisé (un élément-sage de comparaison), tandis que all.equal
compare l'ensemble des vecteurs comme une entité unique.
En utilisant les exemples précédents
a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15, 0.7, 3, 0.15)
==
ne donne pas le "prévu" et all.equal
n'effectuent pas l'élément-sage
> a==b
[1] FALSE FALSE FALSE FALSE
> all.equal(a,b)
[1] "Mean relative difference: 0.01234568"
> isTRUE(all.equal(a,b))
[1] FALSE
Plutôt, une version qui passe en boucle sur les deux vecteurs doivent être utilisés
> mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
[1] TRUE TRUE TRUE FALSE
Si une version fonctionnelle de ce qui est désiré, il peut être écrite
elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
qui peut être appelé comme juste
> elementwise.all.equal(a, b)
[1] TRUE TRUE TRUE FALSE
Ajoutant à Brian le commentaire (ce qui est la raison), vous pouvez venir en utilisant all.equal
à la place:
# i <- 0.1
# i <- i + 0.05
# i
#if(all.equal(i, .15)) cat("i equals 0.15\n") else cat("i does not equal 0.15\n")
#i equals 0.15
Par Joshua avertissement voici le code mis à jour (Merci Josué):
i <- 0.1
i <- i + 0.05
i
if(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines
cat("i equals 0.15\n")
} else {
cat("i does not equal 0.15\n")
}
#i equals 0.15
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.