Testando a igualdade de dois carros alegóricos: exemplo realista

8

Quando normalmente faz sentido na programação testar a igualdade de dois números de ponto flutuante?

ie

a == b 

onde tanto a & b são carros alegóricos.

Minha impressão ingênua é que sempre se testaria a diferença em relação a alguma tolerância epsilon.

Estou errado? O teste da igualdade de carros alegóricos pode ser significativo em certos contextos?

Algum exemplo da natureza? ou seja, de bases de código reais ou aplicativos disponíveis no git etc.

PS. Estou implícito assumindo que o uso do operador de igualdade em carros alegóricos é realmente significativo em alguns contextos; caso contrário, por que a maioria das linguagens de programação o permitiria.

curious_cat
fonte
1
Como se trata de questões práticas com algoritmos numéricos, estou migrando para a Ciência da Computação .
Pode ser útil saber qual o problema que você está tentando resolver fazendo esta pergunta em particular. Meta.stackexchange.com/q/66377
Kirill
1
@ Krill O problema é curiosidade. Conheço os conselhos sobre quando NÃO usá-lo. Mas se o operador ainda for permitido, deve haver casos em que é realmente correto usá-lo. Mas esses casos não eram óbvios. Então eu queria saber. E as respostas trazem alguns bons exemplos.
1313616_16

Respostas:

5

Minha impressão ingênua é que sempre se testaria a diferença em relação a alguma tolerância epsilon.

Uma implementação não ingênua dessa idéia provavelmente deve tirar vantagem do operador de comparação de igualdade para lidar com os casos especiais importantes que o padrão IEEE 754 contempla (infinitos, números desnormalizados ...).

Dê uma olhada em Como devo fazer a comparação de ponto flutuante? :

...

if (a == b)  // shortcut, handles infinities
  return true;

if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
  // a or b is zero or both are extremely close to it
  // relative error is less meaningful here

...


Às vezes, há realmente uma resposta correta e você deseja a igualdade exata. Testar a correção de uma implementação é um bom exemplo (ou seja, existem apenas quatro bilhões de carros alegóricos - portanto, teste todos! ).

manlio
fonte
O teste para um == 0 dentro da cláusula OR é redundante por diff <Float.MIN_NORMAL?
1313616_16
3

Um exemplo óbvio onde ==está ok é quando ae bé o mesmo número a=c; b=c, por exemplo, para verificar se ae bfoi inicializado da mesma maneira. Claro, |a-b| < epsilontambém funcionaria aqui. O único problema é, quão pequeno é epsilon?

Além disso, a == bseria compilado em uma instrução enquanto |a-b| < epsilonlevaria algumas.

Karolis Juodelė
fonte
Obrigado! Quando alguém, em uma situação típica de codificação, gostaria de saber se aeb foram inicializados da mesma maneira? Você pode postar trechos de código usando isso? Quero dizer, não se pode apenas examinar a fonte e contar?
1313616_16:
1
@curious_cat suponha que você esteja criando um jogo como o minecraft, com uma grade de peças, suponha que essas peças tenham coordenadas flutuantes, por qualquer motivo, e que tenham sido inicializadas em um loop óbvio for x in [0..n] step w: for y in [0..n] step w: add_tile(x, y). Nesse caso, t1.x == t2.xé uma maneira perfeitamente segura de testar se os dois blocos estão em um plano. Você só precisa |a-b|<epsilonquando ae bsão resultados de cálculos diferentes que deveriam obter o mesmo valor. Mas muitas vezes você tem o resultado de um cálculo armazenado em duas variáveis ​​ou sobrescreve variáveis ​​com constantes.
Karolis Juodelė
Muito bom exemplo! Obrigado. Faz muito sentido. O primeiro caso de uso de som real que recebi mesmo! Você deve adicionar isso à sua resposta!
1313616_16:
2

Um exemplo extremo: a IBM foi a primeira empresa a construir processadores com uma instrução de adição múltipla combinada. Usando essa instrução, eles criaram um método muito rápido para calcular raízes quadradas de acordo com o padrão IEEE-754. Este método falha para um único valor de entrada 1 ≤ x <4: Se x for o maior número representável como um número de ponto flutuante menor que 4, o resultado será arredondado incorretamente.

Então, em algum lugar de sua implementação, eles verificam se x é igual a um valor específico. Eles querem reconhecer esse valor, e não outros.

gnasher729
fonte
Obrigado! Que valor é esse, alguma ideia? Não encontrei nenhuma referência para ler sobre isso. Você pode postar algum link?
1313616_16
2

Minha impressão ingênua é que sempre se testaria a diferença em relação a alguma tolerância epsilon. Estou errado? O teste da igualdade de carros alegóricos pode ser significativo em certos contextos?

Não há receitas únicas. Em este artigo não é um tratamento exaustivo, onde pode ser encontrada uma resposta completa com técnica e código.

Em resumo, existem principalmente três casos:

  • comparando com zero
  • comparando com um diferente de zero
  • comparando dois números arbitrários

Sua ideia de usar uma comparação contra uma tolerância é boa para alguns casos, mas também há uma técnica baseada na Unidade em último lugar ( ULP ), descrita dentro do artigo

Estou implícito assumindo que o uso do operador de igualdade em carros alegóricos é realmente significativo em alguns contextos; caso contrário, por que a maioria das linguagens de programação o permitiria.

Como acima, há situações em que você pode usá-lo, mas seja avisado. Por exemplo, o compilador gcc possui um aviso:

warning: comparing floating point with == or != is unsafe

Atualizar

Acrescento algumas considerações acima desse argumento e elas não estão estritamente relacionadas ao caso a == b.

Igualdade com expressão

Considerando o caso:

a + b == c 

a b c

umab==cfeu(feu(uma)+feu(b))==feu(c)
  • feu(x)x

|umauma+b|erruma+|buma+b|errb
errx=|x-feu(x)||x|

Portanto, neste caso, o uso de ==é mais delicado.

Portando em ambientes diferentes

Quando portamos um código em ambientes diferentes (máquina diferente), podemos obter resultados diferentes (por exemplo, tente pensar em teste de unidade). Também no caso, o uso de ==é delicado.

Mauro Vanzetto
fonte
Ah! Por isso, gera um aviso. Obrigado. Portanto, minha impressão agora é que, em 99% dos casos de codificação, uma comparação de igualdade de flutuação é um erro, mas ainda é permitida para os poucos casos de canto em que possa estar fazendo sentido.
1313616_16
1
@curious_cat Sim, eu concordo com você. Eu adiciono uma nota.
Mauro Vanzetto 13/11
0

Sua "impressão ingênua" não é uma "impressão ingênua" - é o resultado de ler sobre aritmética de ponto flutuante e entender apenas metade disso. A "impressão ingênua" seria a impressão óbvia de que, para descobrir se a e b são iguais, você pergunta se são iguais.

Há muitas situações em que tudo que você precisa saber é se dois números de ponto flutuante são iguais ou não. Existem muitas situações em que você sabe que não há erros de arredondamento ou variações devido a erros de arredondamento. Como converter números decimais em ponto flutuante, que serão determinísticos em qualquer implementação sã.

Aqui está uma boa: alguém afirmou que, para quaisquer dois números de ponto flutuante a, b, o resultado de (b + a + b) e (b + b + a) são os mesmos e você deseja testar essa afirmação. Tente fazer isso sem comparar dois números de ponto flutuante para igualdade.

Aqui está um exemplo melhor: tente criar um conjunto de números de ponto flutuante.

gnasher729
fonte