Ao comparar valores de ponto flutuante para igualdade, existem duas abordagens diferentes:
NaNnão sendo igual a si próprio, o que corresponde à especificação IEEE 754 .NaNsendo igual a si mesmo, o que fornece a propriedade matemática da Reflexividade, essencial para a definição de uma relação de Equivalência
Os construídas em IEEE flutuante tipos de ponto em C # ( floate double) acompanhar semântica para IEEE ==e !=(e os operadores relacionais como <) mas assegurar reflexividade para object.Equals, IEquatable<T>.Equals(e CompareTo).
Agora considere uma biblioteca que fornece estruturas vetoriais sobre float/ double. Esse tipo de vetor sobrecarregaria ==/ !=e substituía object.Equals/ IEquatable<T>.Equals.
O que todo mundo concorda é que ==/ !=deve seguir a semântica do IEEE. A questão é: essa biblioteca deve implementar o Equalsmétodo (que é separado dos operadores de igualdade) de maneira reflexiva ou de acordo com a semântica do IEEE.
Argumentos para usar a semântica IEEE para Equals:
- Segue IEEE 754
É (possivelmente muito) mais rápido, porque pode tirar proveito das instruções do SIMD
Fiz uma pergunta separada no stackoverflow sobre como você expressaria igualdade reflexiva usando instruções SIMD e seu impacto no desempenho: instruções SIMD para comparação de igualdade de ponto flutuante
Atualização: parece possível implementar a igualdade reflexiva com eficiência usando três instruções SIMD.
A documentação para
Equalsnão requer reflexividade ao envolver ponto flutuante:As instruções a seguir devem ser verdadeiras para todas as implementações do método Equals (Object). Na lista,
x,y, ezrepresentam referências a objetos que não são nulos.x.Equals(x)retornatrue, exceto nos casos que envolvem tipos de ponto flutuante. Consulte ISO / IEC / IEEE 60559: 2011, Tecnologia da informação - Sistemas de microprocessadores - Aritmética de ponto flutuante.Se você estiver usando carros alegóricos como chaves de dicionário, estará vivendo um estado de pecado e não deve esperar um comportamento sensato.
Argumentos para ser reflexivo:
É consistente com os tipos existentes, incluindo
Single,Double,TupleeSystem.Numerics.Complex.Eu não conheço nenhum precedente na BCL onde
Equalssegue o IEEE em vez de ser reflexivo. Contra-exemplos incluemSingle,Double,TupleeSystem.Numerics.Complex.Equalsé usado principalmente por contêineres e algoritmos de busca que dependem da reflexividade. Para esses algoritmos, um ganho de desempenho é irrelevante se os impedir de funcionar. Não sacrifique a correção pelo desempenho.- Rompe todos os conjuntos de hash base e dicionários,
Contains,Find,IndexOfem várias colecções / LINQ, operações conjunto baseado LINQ (Union,Except, etc.), se os dados contémNaNvalores. O código que faz cálculos reais em que a semântica do IEEE é aceitável geralmente funciona em tipos concretos e usa
==/!=(ou comparações epsilon mais prováveis).Atualmente, não é possível gravar cálculos de alto desempenho usando genéricos, pois você precisa de operações aritméticas para isso, mas elas não estão disponíveis por meio de interfaces / métodos virtuais.
Portanto, um
Equalsmétodo mais lento não afetaria a maioria dos códigos de alto desempenho.É possível fornecer um
IeeeEqualsmétodo ou umIeeeEqualityComparer<T>para os casos em que você precisa da semântica IEEE ou da vantagem de desempenho.
Na minha opinião, esses argumentos favorecem fortemente uma implementação reflexiva.
A equipe CoreFX da Microsoft planeja introduzir esse tipo de vetor no .NET. Ao contrário de mim, eles preferem a solução IEEE , principalmente devido às vantagens de desempenho. Como essa decisão certamente não será alterada após o lançamento final, quero receber feedback da comunidade sobre o que acredito ser um grande erro.
float/ doublee vários outros tipos, ==e Equalsjá são diferentes. Eu acho que uma inconsistência com os tipos existentes seria ainda mais confusa do que a inconsistência entre ==e Equalsvocê ainda terá que lidar com outros tipos. 2) Praticamente todos os algoritmos / coleções genéricos usam Equalse dependem de sua reflexividade para funcionar (LINQ e dicionários), enquanto algoritmos concretos de ponto flutuante geralmente usam ==onde obtêm sua semântica IEEE.
Vector<float>um "animal" diferente de um simples floatou double. Por essa medida, não vejo o motivo Equalsou o ==operador de cumprir os padrões deles. Você mesmo disse: "Se você estiver usando carros alegóricos como chaves de dicionário, estará vivendo um estado de pecado e não deve esperar um comportamento sensato". Se alguém armazena NaNem um dicionário, a culpa é deles por usar práticas terríveis. Eu dificilmente acho que a equipe CoreFX não tenha pensado nisso. Eu iria com um ReflexiveEqualsou similar, apenas por uma questão de desempenho.
==eEqualsretornaria resultados diferentes. Muitos programadores assumem que são e fazem a mesma coisa . Além disso - em geral, implementações dos operadores de igualdade invocam oEqualsmétodo. Você argumentou que se pode incluir umIeeeEquals, mas também se pode fazer o contrário e incluir umReflexiveEqualsmétodo. OVector<float>tipo-pode ser usado em muitos aplicativos críticos para o desempenho e deve ser otimizado de acordo.