Dificultad de comparación numérica en R
Estoy tratando de comparar dos números en R como parte de una condición de declaración if:
(a-b) >= 0.5
En este caso particular, a = 0,58 y b = 0,08... y aún así (a-b) >= 0.5
es falso. Soy consciente de los peligros de utilizar ==
comparaciones de números exactos, y esto parece relacionado:
(a - b) == 0.5)
es falso, mientras
all.equal((a - b), 0.5)
es verdad.
La única solución que se me ocurre es tener dos condiciones: (a-b) > 0.5 | all.equal((a-b), 0.5)
. Esto funciona, pero ¿es realmente la única solución? ¿ Debería simplemente renunciar =
para siempre a la familia de operadores de comparación?
Edite para mayor claridad: sé que este es un problema de punto flotante. Más fundamentalmente, lo que pregunto es: ¿qué debo hacer al respecto? ¿Cuál es una forma sensata de lidiar con comparaciones mayores o iguales en R, ya que >=
realmente no se puede confiar en ellas?
Nunca he sido fanático de all.equal
esas cosas. Me parece que a veces la tolerancia funciona de manera misteriosa. ¿Por qué no simplemente comprobar si hay algo mayor que una tolerancia inferior a 0,05?
tol = 1e-5
(a-b) >= (0.05-tol)
En general, sin redondeo y con lógica convencional, encuentro que la lógica directa es mejor que todas.igual
Si x == y
entonces x-y == 0
. Quizás x-y
no sea exactamente 0, así que para tales casos uso
abs(x-y) <= tol
Tienes que establecer la tolerancia de todos modos all.equal
y esto es más compacto y sencillo que all.equal
.
Puede crear esto como un operador separado o sobrescribir la función >= original (probablemente no sea una buena idea) si desea utilizar este enfoque con frecuencia:
# using a tolerance
epsilon <- 1e-10 # set this as a global setting
`%>=%` <- function(x, y) (x + epsilon > y)
# as a new operator with the original approach
`%>=%` <- function(x, y) (all.equal(x, y)==TRUE | (x > y))
# overwriting R's version (not advised)
`>=` <- function(x, y) (isTRUE(all.equal(x, y)) | (x > y))
> (a-b) >= 0.5
[1] TRUE
> c(1,3,5) >= 2:4
[1] FALSE FALSE TRUE
Para completar, señalaré que, en ciertas situaciones, simplemente puedes redondear a unos pocos decimales (y esta es una solución poco convincente en comparación con la mejor solución publicada anteriormente).
round(0.58 - 0.08, 2) == 0.5
Un comentario más. Es all.equal
un genérico. Para valores numéricos, utiliza all.equal.numeric
. Una inspección de esta función muestra que utilizó .Machine$double.eps^0.5
, donde .Machine$double.eps
se define como
double.eps: the smallest positive floating-point number ‘x’ such that
‘1 + x != 1’. It equals ‘double.base ^ ulp.digits’ if either
‘double.base’ is 2 or ‘double.rounding’ is 0; otherwise, it
is ‘(double.base ^ double.ulp.digits) / 2’. Normally
‘2.220446e-16’.
(.Página del manual de la máquina).
En otras palabras, esa sería una opción aceptable para su tolerancia:
myeq <- function(a, b, tol=.Machine$double.eps^0.5)
abs(a - b) <= tol