Lors de la comparaison des valeurs à virgule flottante pour l'égalité, il existe deux approches différentes:
NaNn'é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 # ( floatet 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 Equalsmé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
Equalsne 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,yetzrepré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,TupleetSystem.Numerics.Complex.Je ne connais aucun précédent dans la BCL où
Equalssuit IEEE au lieu d'être réflexif. Les exemples comprennent contreSingle,Double,TupleetSystem.Numerics.Complex.Equalsest 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,IndexOfsur diverses collections / LINQ, ensemble des opérations de LINQ (baseUnion,Exceptetc.) si les données contiennent desNaNvaleurs. 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
Equalsméthode plus lente n'affecterait donc pas la plupart des codes hautes performances.Il est possible de fournir une
IeeeEqualsmé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/ doubleet plusieurs autres types, ==et Equalssont 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 Equalsvous devrez toujours faire face à d'autres types. 2) Presque tous les algorithmes / collections génériques utilisent Equalset 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 floatou double. Par cette mesure, je ne vois pas la raison Equalsou 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 NaNdans 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 ReflexiveEqualsou similaire, juste pour des raisons de performance.
==etEqualsretournerait 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 laEqualsméthode. Vous avez fait valoir que l'on pourrait inclure unIeeeEquals, mais on pourrait aussi le faire dans l'autre sens et inclure uneReflexiveEqualsméthode. LeVector<float>type peut être utilisé dans de nombreuses applications critiques pour les performances et doit être optimisé en conséquence.