Al comparar valores de coma flotante para igualdad, hay dos enfoques diferentes:
NaN
no es igual a sí mismo, lo que coincide con la especificación IEEE 754 .NaN
ser igual a sí mismo, lo que proporciona la propiedad matemática de la reflexividad que es esencial para la definición de una relación de equivalencia
Los tipos de punto flotante IEEE integrados en C # ( float
y double
) siguen la semántica IEEE para ==
y !=
(y los operadores relacionales como <
) pero aseguran la reflexividad para object.Equals
, IEquatable<T>.Equals
(y CompareTo
).
Ahora considere una biblioteca que proporciona estructuras vectoriales sobre float
/ double
. Tal tipo de vector sobrecargaría ==
/ !=
y anularía object.Equals
/ IEquatable<T>.Equals
.
En lo que todos están de acuerdo es que ==
/ !=
debería seguir la semántica de IEEE. La pregunta es, si dicha biblioteca implementa el Equals
método (que está separado de los operadores de igualdad) de una manera reflexiva o que coincida con la semántica IEEE.
Argumentos para usar la semántica IEEE para Equals
:
- Sigue a IEEE 754
Es (posiblemente mucho) más rápido porque puede aprovechar las instrucciones SIMD
He hecho una pregunta por separado sobre stackoverflow sobre cómo expresaría la igualdad reflexiva utilizando las instrucciones SIMD y su impacto en el rendimiento: instrucciones SIMD para la comparación de igualdad de punto flotante
Actualización: Parece que es posible implementar la igualdad reflexiva de manera eficiente utilizando tres instrucciones SIMD.
La documentación para
Equals
no requiere reflexividad cuando involucra punto flotante:Las siguientes declaraciones deben ser ciertas para todas las implementaciones del método Equals (Object). En la lista,
x
,y
, yz
representan referencias a objetos que no son nulos.x.Equals(x)
retornostrue
, excepto en casos que involucran tipos de punto flotante. Ver ISO / IEC / IEEE 60559: 2011, Tecnología de la información - Sistemas de microprocesador - Aritmética de punto flotante.Si usa flotadores como teclas de diccionario, está viviendo en un estado de pecado y no debe esperar un comportamiento sensato.
Argumentos para ser reflexivo:
Es compatible con los tipos existentes, incluyendo
Single
,Double
,Tuple
ySystem.Numerics.Complex
.No conozco ningún precedente en el BCL donde
Equals
siga IEEE en lugar de ser reflexivo. Contraejemplos incluyenSingle
,Double
,Tuple
ySystem.Numerics.Complex
.Equals
es utilizado principalmente por contenedores y algoritmos de búsqueda que dependen de la reflexividad. Para estos algoritmos, una ganancia de rendimiento es irrelevante si les impide funcionar. No sacrifique la corrección por el rendimiento.- Rompe todos los conjuntos y diccionarios en base de hash,
Contains
,Find
,IndexOf
en varias colecciones / LINQ, LINQ operaciones de conjunto basada (Union
,Except
, etc.) si los datos contienenNaN
valores. El código que realiza cálculos reales donde IEEE semántico es aceptable generalmente funciona en tipos y usos concretos
==
/!=
(o más probablemente en comparaciones épsilon).Actualmente no puede escribir cálculos de alto rendimiento utilizando genéricos, ya que necesita operaciones aritméticas para eso, pero estos no están disponibles a través de interfaces / métodos virtuales.
Por lo tanto, un
Equals
método más lento no afectaría a la mayoría de los códigos de alto rendimiento.Es posible proporcionar un
IeeeEquals
método oIeeeEqualityComparer<T>
para los casos en los que necesita la semántica IEEE o necesita una ventaja de rendimiento.
En mi opinión, estos argumentos favorecen fuertemente una implementación reflexiva.
El equipo CoreFX de Microsoft planea introducir dicho tipo de vector en .NET. A diferencia de mí , prefieren la solución IEEE , principalmente debido a las ventajas de rendimiento. Dado que tal decisión ciertamente no cambiará después de un lanzamiento final, quiero recibir comentarios de la comunidad, sobre lo que creo que es un gran error.
float
/ double
y varios otros tipos, ==
y Equals
ya son diferentes. Creo que una inconsistencia con los tipos existentes sería aún más confusa que la inconsistencia entre ==
y Equals
aún tendrá que lidiar con otros tipos. 2) Casi todos los algoritmos / colecciones genéricos usan Equals
y dependen de su reflexividad para funcionar (LINQ y diccionarios), mientras que los algoritmos concretos de punto flotante generalmente usan de ==
dónde obtienen su semántica IEEE.
Vector<float>
una "bestia" diferente a una simple float
o double
. Según esa medida, no puedo ver la razón Equals
o el ==
operador para cumplir con los estándares de ellos. Usted mismo dijo: "Si está usando flotadores como teclas del diccionario, está viviendo en un estado de pecado y no debe esperar un comportamiento sensato". Si uno fuera a almacenar NaN
en un diccionario, entonces es su propia condenada culpa por usar una práctica terrible. No creo que el equipo CoreFX no haya pensado en esto. Yo iría con uno ReflexiveEquals
o similar, solo por el bien del rendimiento.
==
yEquals
devolvería resultados diferentes. Muchos programadores asumen que lo son y hacen lo mismo . Además, en general, las implementaciones de los operadores de igualdad invocan elEquals
método. Usted ha argumentado que uno podría incluir aIeeeEquals
, pero también podría hacerlo al revés e incluir unReflexiveEquals
método-. ElVector<float>
tipo puede usarse en muchas aplicaciones críticas de rendimiento y debe optimizarse en consecuencia.