Pregunta

Estoy tratando de comparar dos números en I como parte de una condición de la sentencia if:

(a-b) >= 0.5

En este caso particular, a = 0,58 y b = 0,08 ... y sin embargo (a-b) >= 0.5 es falsa. Soy consciente de los peligros de usar == para las comparaciones número exacto, y esto parece estar relacionado con:

(a - b) == 0.5) es falsa, mientras

all.equal((a - b), 0.5) es cierto.

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 que realmente la única solución? ¿Debo renuncie a hacer de la familia = de operadores de comparación para siempre?

Editar para mayor claridad: Sé que este es un problema de punto flotante. Más fundamentalmente, lo que pregunto es: ¿qué debo hacer al respecto? ¿Qué es una forma sensata de tratar con mayor que o igual a comparaciones en R, ya que el >= en realidad no puede ser de confianza?

¿Fue útil?

Solución

Nunca he sido un fan de all.equal para tales cosas. Me parece que las obras de tolerancia en forma misteriosa veces. ¿Por qué no acaba de comprobar si hay algo más grande que una tolerancia inferior a 0,05

tol = 1e-5

(a-b) >= (0.05-tol)

En general, sin redondeo y con sólo la lógica convencional me parece lógica recta mejor que all.equal

Si x == y continuación x-y == 0. Tal vez x-y no es exactamente 0 por lo que para estos casos yo uso

abs(x-y) <= tol

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

Otros consejos

Se puede crear esto como un operador independiente o sobrescribir el original> = function (probablemente no es una buena idea) si desea utilizar este método 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 completarlo, voy a señalar que, en ciertas situaciones, usted podría simplemente redonda a unos pocos lugares decimales (y esto es una especie de solución cojo en comparación con la mejor solución previamente publicado.)

round(0.58 - 0.08, 2) == 0.5

Elija un cierto nivel de tolerancia:

epsilon <- 1e-10

A continuación, utilice

(a-b+epsilon) >= 0.5

Sin embargo, si el uso de tolerancias de todos modos, ¿por qué te importa que a-b == 0.5 (de hecho) no se evalúan? Si está utilizando tolerancias de todos modos que usted está diciendo que no se preocupan por los puntos finales exactamente.

Esto es lo que es cierto if ((a-b)> = 0,5) if ((a-b) <0,5)

uno de los siempre debe evaluar verdadera en cada par de dobles. Cualquier código que utiliza uno define implícitamente una ninguna operación en el otro, por lo menos. Si su uso de tolerancias para obtener 0,5 reales incluidos en el primero, pero su problema es definido en un dominio continuo ar no lograr mucho. En la mayoría de los problemas que implican valores continuos en el problema subyacente habrá muy poco sentido de que, puesto que los valores arbitrariamente más de 0,5 siempre evaluar como es debido. Los valores arbitrariamente cerca de 0,5 irán al control de flujo "malo", pero en los problemas continuos en la que utiliza la precisión adecuada que no importa.

El único momento en que las tolerancias tienen sentido cuando se trata de problemas del tipo if ((a-b) == c) if ((a-b)! = c)

A continuación, ninguna cantidad de "precisión adecuada" puede ayudar. La razón es que usted tiene que estar preparado que el segundo siempre se evaluará como true a menos que establezca los bits de un-b en un nivel muy bajo con la mano, cuando en realidad es probable que desee el primero en ser a veces es cierto.

Uno de los comentarios más. El all.equal es un genérico. Para valores numéricos, que 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’.

(. Máquina página del manual).

En otras palabras, que sería una opción aceptable para su tolerancia:

myeq <- function(a, b, tol=.Machine$double.eps^0.5)
      abs(a - b) <= tol
comparaciones

<= y >= no son específicos de idioma cuando dificultad numérica se eleva en los números de punto flotante.

IsSmallerOrEqual <- function(a,b) {   # To check a <= b
# Check whether "Mean relative difference..." exist in all.equal's result; 
# If exists, it results in character, not logical
if (   class(all.equal(a, b)) == "logical" && (a<b | all.equal(a, b))) { return(TRUE)
 } else if (a < b) { return(TRUE)
     } else { return(FALSE) }
}

IsSmallerOrEqual(abs(-2-(-2.2)), 0.2) # TRUE; To check |-2-(-2.2)| <= 0.2
IsSmallerOrEqual(abs(-2-(-2.2)), 0.3) # TRUE
IsSmallerOrEqual(abs(-2-(-2.2)), 0.1) # FALSE

IsBiggerOrEqual  <- function(a,b) {   # To check a >= b
# Check whether "Mean relative difference..." exist in all.equal's result; 
# If exists, it results in character, not logical
if (   class(all.equal(a, b)) == "logical" && (a>b | all.equal(a, b))) { return(TRUE)
 } else if (a > b) { return(TRUE)
     } else { return(FALSE) }
}
IsBiggerOrEqual(3,3) # TRUE
IsBiggerOrEqual(4,3) # TRUE
IsBiggerOrEqual(3,4) # FALSE
IsBiggerOrEqual(0.58 - 0.08,0.5)  # TRUE

Si all.equal no se procesa, se puede encontrar errores.

La siguiente no es necesario pero útil:

abs(-2-(-2.2)) # 0.2

sprintf("%.54f",abs(-2-(-2.2)))  # "0.200000000000000177635683940025046467781066894531250000"
sprintf("%.54f",0.2)             # "0.200000000000000011102230246251565404236316680908203125"

all.equal(abs(-2-(-2.2)), 0.2)  # TRUE; check nearly equivalence of floating point numbers
identical(abs(-2-(-2.2)), 0.2)  # FALSE; check exact equivalence of floating point numbers
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top