Beim Vergleich von Gleitkommawerten auf Gleichheit gibt es zwei verschiedene Ansätze:
NaN
nicht gleich sich selbst sein, was der IEEE 754- Spezifikation entspricht.NaN
Gleichheit mit sich selbst, die die mathematische Eigenschaft der Reflexivität liefert, die für die Definition einer Äquivalenzbeziehung wesentlich ist
Die eingebauten IEEE-Gleitkommatypen in C # ( float
und double
) folgen der IEEE-Semantik für ==
und !=
(und den relationalen Operatoren wie <
), gewährleisten jedoch die Reflexivität für object.Equals
, IEquatable<T>.Equals
(und CompareTo
).
Betrachten Sie nun eine Bibliothek, die Vektorstrukturen über float
/ bereitstellt double
. Ein solcher Vektortyp würde ==
/ !=
und object.Equals
/ überschreiben IEquatable<T>.Equals
.
Alle sind sich einig, dass ==
/ !=
sollte der IEEE-Semantik folgen. Die Frage ist, Equals
ob eine solche Bibliothek die Methode (die von den Gleichheitsoperatoren getrennt ist) auf eine Weise implementiert , die reflexiv ist oder der IEEE-Semantik entspricht.
Argumente für die Verwendung der IEEE-Semantik für Equals
:
- Es folgt IEEE 754
Es ist (möglicherweise viel) schneller, weil es SIMD-Anweisungen nutzen kann
Ich habe eine separate Frage zum Stapelüberlauf gestellt, wie Sie die reflexive Gleichheit mithilfe von SIMD-Anweisungen und deren Auswirkungen auf die Leistung ausdrücken würden: SIMD-Anweisungen für den Gleitkomma-Gleichheitsvergleich
Update: Es scheint möglich zu sein, die reflexive Gleichheit mithilfe von drei SIMD-Anweisungen effizient zu implementieren.
Die Dokumentation für
Equals
erfordert keine Reflexivität, wenn Gleitkomma verwendet wird:Die folgenden Aussagen müssen für alle Implementierungen der Equals (Object) -Methode zutreffen. In der Liste
x
,y
undz
Objektreferenzen darstellen , die nicht null sind.x.Equals(x)
Gibt zurücktrue
, außer in Fällen, in denen Gleitkommatypen verwendet werden. Siehe ISO / IEC / IEEE 60559: 2011, Informationstechnologie - Mikroprozessorsysteme - Gleitkomma-Arithmetik.Wenn Sie Floats als Wörterbuchschlüssel verwenden, leben Sie in einem Zustand der Sünde und sollten kein vernünftiges Verhalten erwarten.
Argumente für Reflexivität:
Es ist im Einklang mit dem bestehenden Typen, einschließlich
Single
,Double
,Tuple
undSystem.Numerics.Complex
.Ich kenne keinen Präzedenzfall in der BCL, in
Equals
dem IEEE folgt, anstatt reflexiv zu sein. Gegenbeispiele sindSingle
,Double
,Tuple
undSystem.Numerics.Complex
.Equals
wird hauptsächlich von Containern und Suchalgorithmen verwendet, die auf Reflexivität beruhen. Für diese Algorithmen ist ein Leistungsgewinn irrelevant, wenn sie nicht funktionieren. Opfern Sie nicht die Korrektheit für die Leistung.- Es bricht alle Hash - basierte Sets und Wörterbücher,
Contains
,Find
,IndexOf
auf verschiedenen Sammlungen / LINQ, Satz basiert LINQ - Operationen (Union
,Except
usw.) , wenn die Daten enthältNaN
Werte. Code, der tatsächliche Berechnungen durchführt, bei denen die IEEE-Semantik akzeptabel ist, funktioniert normalerweise mit konkreten Typen und Verwendungen
==
/!=
(oder wahrscheinlicher Epsilon-Vergleichen).Sie können derzeit keine Hochleistungsberechnungen mit Generika schreiben, da Sie dafür arithmetische Operationen benötigen, diese sind jedoch nicht über Schnittstellen / virtuelle Methoden verfügbar.
Eine langsamere
Equals
Methode würde sich also nicht auf die meisten Hochleistungscodes auswirken.Es ist möglich, eine
IeeeEquals
Methode oder eineIeeeEqualityComparer<T>
für die Fälle bereitzustellen, in denen Sie entweder die IEEE-Semantik oder einen Leistungsvorteil benötigen.
Meiner Meinung nach sprechen diese Argumente stark für eine reflexive Umsetzung.
Das CoreFX-Team von Microsoft plant die Einführung eines solchen Vektortyps in .NET. Im Gegensatz zu mir bevorzugen sie die IEEE-Lösung , hauptsächlich aufgrund der Leistungsvorteile. Da eine solche Entscheidung nach einer endgültigen Veröffentlichung sicherlich nicht geändert wird, möchte ich Feedback von der Community erhalten, was ich für einen großen Fehler halte.
float
/ double
und einige andere Typen ==
und Equals
sind bereits unterschiedlich. Ich denke, eine Inkonsistenz mit vorhandenen Typen wäre noch verwirrender als die Inkonsistenz zwischen ==
und Equals
Sie müssen sich immer noch mit anderen Typen befassen. 2) Nahezu alle generischen Algorithmen / Sammlungen verwenden Equals
und verlassen sich auf ihre Funktionsreflexivität (LINQ und Wörterbücher), während konkrete Gleitkomma-Algorithmen normalerweise dort verwendet werden, ==
wo sie ihre IEEE-Semantik erhalten.
Vector<float>
ein anderes "Biest" in Betracht ziehen als ein einfaches float
oder double
. Durch diese Maßnahme kann ich den Grund Equals
oder den ==
Betreiber nicht erkennen , die Standards von ihnen einzuhalten. Sie sagten selbst: "Wenn Sie Floats als Wörterbuchschlüssel verwenden, leben Sie in einem Zustand der Sünde und sollten kein vernünftiges Verhalten erwarten." Wenn man NaN
in einem Wörterbuch speichert , dann ist es ihre eigene verdammte Schuld, schreckliche Praktiken anzuwenden. Ich denke kaum, dass das CoreFX-Team dies nicht durchdacht hat. Ich würde mit einem ReflexiveEquals
oder ähnlichem gehen, nur um der Leistung willen.
==
undEquals
würde unterschiedliche Ergebnisse liefern. Viele Programmierer gehen davon aus und tun dasselbe . Darüber hinaus rufen Implementierungen der Gleichheitsoperatoren im Allgemeinen dieEquals
Methode auf. Sie haben argumentiert, dass man eine einschließen könnteIeeeEquals
, aber man könnte es auch umgekehrt machen und eine Methode einschließenReflexiveEquals
. DerVector<float>
Typ kann in vielen leistungskritischen Anwendungen verwendet werden und sollte entsprechend optimiert werden.