Lors de la comparaison des valeurs à virgule flottante pour l'égalité, il existe deux approches différentes:
NaN
n'étant pas égal à lui-même, ce qui correspond à la spécification IEEE 754 .NaN
étant égal à lui-même, ce qui fournit la propriété mathématique de la réflexivité qui est essentielle à la définition d'une relation d'équivalence
Les types à virgule flottante IEEE intégrés en C # ( float
et double
) suivent la sémantique IEEE pour ==
et !=
(et les opérateurs relationnels comme <
) mais assurent la réflexivité pour object.Equals
, IEquatable<T>.Equals
(et CompareTo
).
Considérons maintenant une bibliothèque qui fournit des structures vectorielles au-dessus de float
/ double
. Un tel type de vecteur surchargerait ==
/ !=
et remplacerait object.Equals
/ IEquatable<T>.Equals
.
Ce sur quoi tout le monde est d'accord, c'est que ==
/ !=
devrait suivre la sémantique de l'IEEE. La question est de savoir si une telle bibliothèque doit implémenter la Equals
méthode (qui est distincte des opérateurs d'égalité) d'une manière réflexive ou d'une manière qui correspond à la sémantique IEEE.
Arguments pour utiliser la sémantique IEEE pour Equals
:
- Il suit IEEE 754
C'est (peut-être beaucoup) plus rapide car il peut tirer parti des instructions SIMD
J'ai posé une question distincte sur stackoverflow sur la façon dont vous exprimeriez l'égalité réflexive à l'aide d'instructions SIMD et leur impact sur les performances: instructions SIMD pour la comparaison d'égalité en virgule flottante
Mise à jour: Il semble possible d'implémenter efficacement l'égalité réflexive à l'aide de trois instructions SIMD.
La documentation de
Equals
ne nécessite pas de réflexivité lors de l'utilisation de virgule flottante:Les instructions suivantes doivent être vraies pour toutes les implémentations de la méthode Equals (Object). Dans la liste,
x
,y
etz
représentent des références d'objets qui ne sont pas nuls.x.Equals(x)
renvoietrue
, sauf dans les cas qui impliquent des types à virgule flottante. Voir ISO / CEI / IEEE 60559: 2011, Technologies de l'information - Systèmes à microprocesseurs - Arithmétique à virgule flottante.Si vous utilisez des flottants comme clés de dictionnaire, vous vivez dans un état de péché et ne devez pas vous attendre à un comportement sain.
Arguments pour être réflexif:
Il est compatible avec les types existants, y compris
Single
,Double
,Tuple
etSystem.Numerics.Complex
.Je ne connais aucun précédent dans la BCL où
Equals
suit IEEE au lieu d'être réflexif. Les exemples comprennent contreSingle
,Double
,Tuple
etSystem.Numerics.Complex
.Equals
est principalement utilisé par les conteneurs et les algorithmes de recherche qui reposent sur la réflexivité. Pour ces algorithmes, un gain de performances n'est pas pertinent s'il les empêche de fonctionner. Ne sacrifiez pas l'exactitude à la performance.- Il casse tous les ensembles à base de hachage et des dictionnaires,
Contains
,Find
,IndexOf
sur diverses collections / LINQ, ensemble des opérations de LINQ (baseUnion
,Except
etc.) si les données contiennent desNaN
valeurs. Le code qui effectue des calculs réels où la sémantique IEEE est acceptable fonctionne généralement sur des types concrets et utilise
==
/!=
(ou des comparaisons epsilon plus probables).Actuellement, vous ne pouvez pas écrire de calculs haute performance à l'aide de génériques car vous avez besoin d'opérations arithmétiques pour cela, mais celles-ci ne sont pas disponibles via des interfaces / méthodes virtuelles.
Une
Equals
méthode plus lente n'affecterait donc pas la plupart des codes hautes performances.Il est possible de fournir une
IeeeEquals
méthode ou uneIeeeEqualityComparer<T>
pour les cas où vous avez besoin de la sémantique IEEE ou si vous avez besoin d'un avantage en termes de performances.
À mon avis, ces arguments favorisent fortement une mise en œuvre réflexive.
L'équipe CoreFX de Microsoft prévoit d'introduire un tel type de vecteur dans .NET. Contrairement à moi, ils préfèrent la solution IEEE , principalement en raison des avantages de performance. Puisqu'une telle décision ne sera certainement pas modifiée après une version finale, je veux obtenir des commentaires de la communauté sur ce que je pense être une grosse erreur.
float
/ double
et plusieurs autres types, ==
et Equals
sont déjà différents. Je pense qu'une incohérence avec les types existants serait encore plus déroutante que l'incohérence entre ==
et Equals
vous devrez toujours faire face à d'autres types. 2) Presque tous les algorithmes / collections génériques utilisent Equals
et s'appuient sur sa réflexivité pour fonctionner (LINQ et dictionnaires), tandis que les algorithmes concrets à virgule flottante utilisent généralement ==
où ils obtiennent leur sémantique IEEE.
Vector<float>
une "bête" différente d'un simple float
ou double
. Par cette mesure, je ne vois pas la raison Equals
ou l' ==
opérateur de se conformer à leurs normes. Vous vous êtes dit: "Si vous utilisez des flotteurs comme clés de dictionnaire, vous vivez dans un état de péché et ne vous attendez pas à un comportement sain". Si l'on devait stocker NaN
dans un dictionnaire, c'est leur propre faute pour avoir utilisé une pratique terrible. Je pense à peine que l'équipe CoreFX n'y a pas réfléchi. J'irais avec un ReflexiveEquals
ou similaire, juste pour des raisons de performance.
==
etEquals
retournerait des résultats différents. De nombreux programmeurs supposent qu'ils le sont et font la même chose . De plus - en général, les implémentations des opérateurs d'égalité invoquent laEquals
méthode. Vous avez fait valoir que l'on pourrait inclure unIeeeEquals
, mais on pourrait aussi le faire dans l'autre sens et inclure uneReflexiveEquals
méthode. LeVector<float>
type peut être utilisé dans de nombreuses applications critiques pour les performances et doit être optimisé en conséquence.