Dificuldade de comparação numérica em R

Estou tentando comparar dois números em R como parte de uma condição if-statement:

(ab) >= 0.5

Neste caso particular, a = 0,58 eb = 0,08 … e ainda assim (ab) >= 0.5 é falso. Estou ciente dos perigos de usar == para comparações exatas de números, e isso parece relacionado:

(a - b) == 0.5) é falso, enquanto

all.equal((a - b), 0.5) é verdadeiro.

A única solução que consigo pensar é ter duas condições: (ab) > 0.5 | all.equal((ab), 0.5) (ab) > 0.5 | all.equal((ab), 0.5) . Isso funciona, mas essa é realmente a única solução? Devo apenas jurar fora da família de operadores de comparação para sempre?

Editar para maior clareza: Eu sei que este é um problema de ponto flutuante. Mais fundamentalmente, o que estou perguntando é: o que devo fazer sobre isso? Qual é uma maneira sensata de lidar com comparações maiores do que ou iguais em R, já que o >= não pode ser realmente confiável?

Eu nunca fui fã de all.equal essas coisas. Parece-me que a tolerância funciona de maneiras misteriosas às vezes. Por que não apenas verificar algo maior que uma tolerância menor que 0,05

 tol = 1e-5 (ab) >= (0.05-tol) 

Em geral, sem arredondamento e apenas com a lógica convencional, eu acho a lógica direta melhor que todas as outras.

Se x == y então xy == 0 . Talvez xy não é exatamente 0 assim, para tais casos eu uso

 abs(xy) < = tol 

Você tem que definir a tolerância de qualquer maneira para todos os all.equal e isso é mais compacto e simples do que todos os all.equal .

Você poderia criar isto como um operador separado ou sobrescrever a function original = = (provavelmente não é uma boa idéia) se você quiser usar esta abordagem freqüentemente:

 # 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)) > (ab) >= 0.5 [1] TRUE > c(1,3,5) >= 2:4 [1] FALSE FALSE TRUE 

Para fins de integralidade, vou ressaltar que, em certas situações, você poderia simplesmente arredondar para algumas casas decimais (e isso é meio que uma solução fraca em comparação à melhor solução publicada anteriormente).

 round(0.58 - 0.08, 2) == 0.5 

Escolha algum nível de tolerância:

 epsilon < - 1e-10 

Então use

 (a-b+epsilon) >= 0.5 

Mas, se você usa tolerâncias de qualquer maneira, por que você se importa que ab == .5 (na verdade) não seja avaliado? Se você está usando tolerâncias de qualquer maneira, você está dizendo que eu não me importo com os pontos finais exatamente.

Aqui está o que é verdadeiro se ((ab)> = .5) if ((ab) < .5)

um deles deve sempre avaliar a verdade em cada par de duplas. Qualquer código que use um define implicitamente uma operação no no outro, pelo menos. Se você estiver usando tolerâncias para obter 0,5 reais incluídos no primeiro, mas seu problema é definido em um domínio contínuo, você não está realizando muito. Na maioria dos problemas que envolvem valores contínuos no problema subjacente, haverá muito pouco sentido para isso, uma vez que valores arbitrariamente acima de 0,5 sempre serão avaliados como deveriam. Valores arbitrariamente próximos de 0,5 vão para o controle de stream “errado”, mas em problemas contínuos onde você está usando precisão apropriada que não importa.

A única vez que as tolerâncias fazem sentido é quando você está lidando com problemas do tipo if ((ab) == c) if ((ab)! = C)

Aqui, nenhuma quantidade de “precisão apropriada” pode ajudá-lo. A razão é que você tem que estar preparado para que o segundo seja sempre avaliado como verdadeiro, a menos que você configure os bits de ab a um nível muito baixo à mão, quando na verdade você provavelmente quer que o primeiro seja às vezes verdadeiro.

Mais um comentário. O all.equal é um genérico. Para valores numéricos, usa all.equal.numeric . Uma inspeção desta function mostra que ela usou .Machine$double.eps^0.5 , onde .Machine$double.eps é definida 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 de manual .Machine).

Em outras palavras, isso seria uma escolha aceitável para sua tolerância:

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