При порівнянні значень з плаваючою комою для рівності існує два різних підходи:
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>тип може бути використаний у багатьох критично важливих для роботи додатках, і його слід оптимізувати відповідно.