Beim Vergleich von Gleitkommawerten auf Gleichheit gibt es zwei verschiedene Ansätze:
NaNnicht gleich sich selbst sein, was der IEEE 754- Spezifikation entspricht.NaNGleichheit 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 # ( floatund 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, Equalsob 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
Equalserfordert 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,yundzObjektreferenzen 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,TupleundSystem.Numerics.Complex.Ich kenne keinen Präzedenzfall in der BCL, in
Equalsdem IEEE folgt, anstatt reflexiv zu sein. Gegenbeispiele sindSingle,Double,TupleundSystem.Numerics.Complex.Equalswird 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,IndexOfauf verschiedenen Sammlungen / LINQ, Satz basiert LINQ - Operationen (Union,Exceptusw.) , wenn die Daten enthältNaNWerte. 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
EqualsMethode würde sich also nicht auf die meisten Hochleistungscodes auswirken.Es ist möglich, eine
IeeeEqualsMethode 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/ doubleund einige andere Typen ==und Equalssind bereits unterschiedlich. Ich denke, eine Inkonsistenz mit vorhandenen Typen wäre noch verwirrender als die Inkonsistenz zwischen ==und EqualsSie müssen sich immer noch mit anderen Typen befassen. 2) Nahezu alle generischen Algorithmen / Sammlungen verwenden Equalsund 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 floatoder double. Durch diese Maßnahme kann ich den Grund Equalsoder 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 NaNin 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 ReflexiveEqualsoder ähnlichem gehen, nur um der Leistung willen.
==undEqualswürde unterschiedliche Ergebnisse liefern. Viele Programmierer gehen davon aus und tun dasselbe . Darüber hinaus rufen Implementierungen der Gleichheitsoperatoren im Allgemeinen dieEqualsMethode 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.