Dificultad de comparación numérica en R

Resuelto Matt Parker asked hace 54 años • 8 respuestas

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.5es 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?

Matt Parker avatar Jan 01 '70 08:01 Matt Parker
Aceptado

Nunca he sido fanático de all.equalesas 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 == yentonces x-y == 0. Quizás x-yno sea exactamente 0, así que para tales casos uso

abs(x-y) <= tol

Tienes que establecer la tolerancia de todos modos all.equaly esto es más compacto y sencillo que all.equal.

John avatar May 05 '2010 00:05 John

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
Shane avatar May 04 '2010 23:05 Shane

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
icio avatar May 05 '2010 00:05 icio

Un comentario más. Es all.equalun 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.epsse 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
January avatar Apr 19 '2017 07:04 January