При порівнянні значень з плаваючою комою для рівності існує два різних підходи:
NaN
не дорівнює собі, що відповідає специфікації IEEE 754 .NaN
рівність собі, що забезпечує математичну властивість рефлексивності, що має істотне значення для визначення відношення еквівалентності
Вбудований в IEEE з плаваючою точкою типу в C # ( float
і double
) слід IEEE семантиці ==
і !=
(і реляційні оператори , як <
) , але забезпечити повернення для object.Equals
, IEquatable<T>.Equals
(і CompareTo
).
Тепер розглянемо бібліотеку, яка забезпечує векторні структури поверх float
/ double
. Такий векторний тип би перевантажував ==
/ !=
і переосмислював object.Equals
/ IEquatable<T>.Equals
.
З чим усі згодні, це те, що ==
/ !=
слід слідувати семантиці IEEE. Питання полягає в тому, чи повинна така бібліотека реалізовувати Equals
метод (який є окремим від операторів рівності) таким чином, що рефлексивно або таким чином, що відповідає семантиці IEEE.
Аргументи для використання семантики IEEE для Equals
:
- Звідси випливає IEEE 754
Це (можливо, набагато швидше), оскільки він може скористатися інструкціями SIMD
Я задав окреме запитання щодо stackoverflow про те, як би ви виражали рефлексивну рівність, використовуючи інструкції SIMD та їх вплив на продуктивність: інструкції SIMD для порівняння рівності з плаваючою комою
Оновлення: Схоже, можна ефективно реалізувати рефлексивну рівність за допомогою трьох інструкцій SIMD.
Документація для
Equals
не вимагає рефлексивності під час залучення плаваючої точки:Наступні твердження повинні бути правдивими для всіх реалізацій методу рівнянь (Object). У списку
x
,y
іz
являють собою посилання на об'єкти, які не є порожніми.x.Equals(x)
поверненняtrue
, за винятком випадків, що стосуються типів з плаваючою комою. Див. ISO / IEC / IEEE 60559: 2011, Інформаційні технології - Мікропроцесорні системи - Арифметика з плаваючою комою.Якщо ви використовуєте поплавці як словникові клавіші, ви живете в стані гріха і не повинні сподіватися на розумну поведінку.
Аргументи рефлексивності:
Це узгоджується з існуючими типами, в тому числі
Single
,Double
,Tuple
іSystem.Numerics.Complex
.Я не знаю жодного прецеденту в BCL, де
Equals
слід IEEE, а не рефлексивно. Лічильник приклади включаютьSingle
,Double
,Tuple
іSystem.Numerics.Complex
.Equals
в основному використовується контейнерами та алгоритмами пошуку, які покладаються на рефлексивність. Для цих алгоритмів підвищення продуктивності не має значення, якщо заважає їм працювати. Не жертвуйте правильністю виконання.- Він ламає всі хеш на основі наборів і словники,
Contains
,Find
,IndexOf
на різних збірках / LINQ, набір на основі LINQ операції (Union
,Except
і т.д.) , якщо дані містятьNaN
значення. Код, який робить фактичні обчислення, де IEEE семантичний прийнятний, зазвичай працює на конкретних типах і використовує
==
/!=
(або, скоріше, порівняння epsilon).Наразі ви не можете писати обчислення високої продуктивності, використовуючи дженерики, оскільки для цього вам потрібні арифметичні операції, але вони недоступні через інтерфейси / віртуальні методи.
Так повільніший
Equals
метод не вплине на більшість високопродуктивних кодів.Можна запропонувати
IeeeEquals
метод абоIeeeEqualityComparer<T>
для тих випадків, коли вам потрібна семантика IEEE або вам потрібно мати перевагу у виконанні.
На мій погляд, ці аргументи сильно сприяють рефлексивному виконанню.
Команда CoreFX Microsoft планує впровадити такий векторний тип у .NET. На відміну від мене, вони віддають перевагу рішенню IEEE , головним чином завдяки перевагам у роботі. Оскільки таке рішення, безумовно, не буде змінено після остаточного випуску, я хочу отримати відгук громади про те, що я вважаю великою помилкою.
float
/ double
та декількох інших типів, ==
і Equals
вже різні. Я думаю, що невідповідність існуючим типам буде навіть більш заплутаною, ніж невідповідність між ними, ==
і Equals
вам доведеться все-таки мати справу з іншими типами. 2) В основному всі загальні алгоритми / колекції використовують Equals
і покладаються на його рефлексивність до функціонування (LINQ та словники), тоді як конкретні алгоритми з плаваючою комою зазвичай використовують ==
там, де вони отримують свою семантику IEEE.
Vector<float>
би іншого «звіра», ніж простого float
або double
. За цією мірою я не бачу причин Equals
або ==
оператор дотримуватись цих стандартів. Ви самі сказали: "Якщо ви використовуєте поплавці як клавіші словника, ви живете в стані гріха і не повинні очікувати розумної поведінки". Якщо потрібно зберігати NaN
в словнику, то це їхня сама проклята вина за використання жахливої практики. Я навряд чи думаю, що команда CoreFX не продумала цього. Я б поїхав з ReflexiveEquals
чи подібним, просто заради виконання.
==
іEquals
повертає різні результати. Багато програмістів припускають, що вони є, і роблять те саме . Крім того - загалом, реалізація операторів рівності посилається наEquals
метод. Ви стверджували, що можна включити aIeeeEquals
, але можна також зробити це навпаки і включитиReflexiveEquals
-метод. ЦейVector<float>
тип може бути використаний у багатьох критично важливих для роботи додатках, і його слід оптимізувати відповідно.