Porównując wartości zmiennoprzecinkowe dla równości, istnieją dwa różne podejścia:
NaNnie są sobie równe, co odpowiada specyfikacji IEEE 754 .NaNbędąc równym sobie, co zapewnia matematyczną właściwość Refleksyjności, która jest niezbędna do zdefiniowania relacji Równoważności
Wbudowane typy zmiennoprzecinkowe IEEE w języku C # ( floati double) są zgodne z semantyką IEEE dla ==i !=(oraz operatorów relacyjnych, takich jak <), ale zapewniają zwrotność dla object.Equals, IEquatable<T>.Equals(i CompareTo).
Rozważmy teraz bibliotekę, która zawiera struktury wektorowe na float/ double. Taki typ wektora spowodowałby przeciążenie ==/ !=i zastąpienie object.Equals/ IEquatable<T>.Equals.
Co każdy zgadza się na to, że ==/ !=powinny być zgodne IEEE semantykę. Pytanie brzmi, czy taka biblioteka powinna implementować Equalsmetodę (która jest niezależna od operatorów równości) w sposób zwrotny lub zgodny z semantyką IEEE.
Argumenty za zastosowaniem semantyki IEEE dla Equals:
- Jest zgodny z IEEE 754
Jest (prawdopodobnie znacznie) szybszy, ponieważ może korzystać z instrukcji SIMD
Zadałem osobne pytanie na temat przepełnienia stosu dotyczące sposobu wyrażenia równości zwrotnej za pomocą instrukcji SIMD i ich wpływu na wydajność: instrukcje SIMD do porównania zmiennoprzecinkowego
Aktualizacja: Wydaje się, że możliwe jest skuteczne wdrożenie równości zwrotnej przy użyciu trzech instrukcji SIMD.
Dokumentacja dotycząca
Equalsnie wymaga zwrotności w przypadku zmiennoprzecinkowego:Poniższe instrukcje muszą być prawdziwe dla wszystkich implementacji metody Equals (Object). Na liście
x,yizreprezentują odwołań do obiektów, które nie są puste.x.Equals(x)zwracatrue, z wyjątkiem przypadków, które dotyczą typów zmiennoprzecinkowych. Patrz ISO / IEC / IEEE 60559: 2011, Informatyka - Systemy mikroprocesorowe - Arytmetyka zmiennoprzecinkowa.Jeśli używasz liczb zmiennoprzecinkowych jako kluczy słownikowych, żyjesz w stanie grzechu i nie powinieneś oczekiwać rozsądnego zachowania.
Argumenty za byciem zwrotnym:
Jest to zgodne z istniejących typów, w tym
Single,Double,TupleiSystem.Numerics.Complex.Nie znam żadnego precedensu w BCL, w którym
Equalsnastępuje IEEE zamiast bycia refleksyjnym. Licznik przykłady obejmująSingle,Double,TupleiSystem.Numerics.Complex.Equalsjest najczęściej używany przez kontenery i algorytmy wyszukiwania, które opierają się na zwrotności. W przypadku tych algorytmów wzrost wydajności nie ma znaczenia, jeśli uniemożliwia im działanie. Nie poświęcajcie poprawności dla wydajności.- Łamie ona wszystkie zestawy oparte hash i słowniki,
Contains,Find,IndexOfna różnych zbiorach / LINQ, zestaw operacji na bazie (LINQUnion,Exceptitp), jeśli dane zawierająNaNwartości. Kod wykonujący rzeczywiste obliczenia, w których semantyczny IEEE jest akceptowalny, zwykle działa na konkretnych typach i wykorzystuje
==/!=(lub bardziej prawdopodobne porównania epsilon).Obecnie nie można pisać obliczeń o wysokiej wydajności przy użyciu ogólnych, ponieważ potrzebne są do tego operacje arytmetyczne, ale nie są one dostępne za pośrednictwem interfejsów / metod wirtualnych.
Dlatego wolniejsza
Equalsmetoda nie wpłynie na większość kodu o wysokiej wydajności.Możliwe jest podanie
IeeeEqualsmetody lubIeeeEqualityComparer<T>dla przypadków, w których albo potrzebujesz semantyki IEEE, albo potrzebujesz przewagi wydajności.
Moim zdaniem argumenty te zdecydowanie sprzyjają refleksyjnej realizacji.
Zespół Microsoft CoreFX planuje wprowadzić taki typ wektora w .NET. W przeciwieństwie do mnie preferują rozwiązanie IEEE , głównie ze względu na zalety wydajnościowe. Ponieważ taka decyzja z pewnością nie ulegnie zmianie po ostatecznym wydaniu, chcę uzyskać informacje zwrotne od społeczności na temat tego, co uważam za duży błąd.
float/ doublei kilku innych typów ==i Equalsjuż są różne. Myślę, że niespójność z istniejącymi typami byłaby nawet bardziej myląca niż niespójność między ==i Equalsnadal będziesz musiał radzić sobie z innymi typami. 2) Prawie wszystkie ogólne algorytmy / kolekcje używają Equalsi polegają na swojej zwrotności do działania (LINQ i słowniki), podczas gdy konkretne algorytmy zmiennoprzecinkowe zwykle używają ==tam, gdzie uzyskują semantykę IEEE.
Vector<float>inną „bestię” niż zwykłą floatlub double. Dzięki takiemu rozwiązaniu nie widzę powodu Equalsani ==operatora do przestrzegania ich standardów. Sam powiedziałeś: „Jeśli używasz liczb zmiennoprzecinkowych jako kluczy słownikowych, żyjesz w stanie grzechu i nie powinieneś oczekiwać rozsądnego zachowania”. Jeśli ktoś miałby przechowywać NaNw słowniku, to ich cholerna wina za stosowanie okropnej praktyki. Nie sądzę, żeby zespół CoreFX nie przemyślał tego. Wybrałbym coś ReflexiveEqualspodobnego, tylko ze względu na wydajność.
==iEqualszwróciłoby inne wyniki. Wielu programistów zakłada, że są i robią to samo . Ponadto - ogólnie, implementacje operatorów równości przywołują tęEqualsmetodę. Argumentowałeś, że można dołączyć aIeeeEquals, ale można to zrobić na odwrót i dołączyć metodęReflexiveEquals. TenVector<float>typ może być używany w wielu aplikacjach krytycznych pod względem wydajności i powinien zostać odpowiednio zoptymalizowany.