c# - Почему.NET 4.0 сортирует этот массив по-другому, чем.NET 3.5?




(3)

[Это мой ответ, бесстыдный, разорванный с другого поста. Было бы хорошо, если кто-то изучил это дальше - double.CompareTo и double.CompareTo(double) четко определены, как указано ниже, поэтому я подозреваю, что существует некоторая магия Array.Sort для определенного типа.]

Array.Sort(double[]) : кажется, не использует CompareTo(double[]) как ожидалось, и это может быть очень ошибкой - обратите внимание на разницу в Array.Sort (object []) и Array.Sort (двойной []) ниже . Мне хотелось бы уточнить / исправить следующее.

Во-первых, документация double.CompareTo(T) - это упорядочение хорошо определено в соответствии с документацией :

Меньше нуля : этот экземпляр меньше значения. -or- Этот экземпляр не является числом (NaN), а значением является число.

Zero : этот экземпляр равен значению. -или- Оба этого экземпляра и значения не являются числом (NaN), PositiveInfinity или NegativeInfinity.

Больше нуля : этот экземпляр больше значения. -или- Этот экземпляр - это число и значение, а не число (NaN).

В LINQPad (3.5 и 4 оба имеют одинаковые результаты):

0d.CompareTo(0d).Dump();                  // 0
double.NaN.CompareTo(0d).Dump();          // -1
double.NaN.CompareTo(double.NaN).Dump();  // 0
0d.CompareTo(double.NaN).Dump();          // 1

Использование CompareTo(object) имеет те же результаты:

0d.CompareTo((object)0d).Dump();                  // 0
double.NaN.CompareTo((object)0d).Dump();          // -1
double.NaN.CompareTo((object)double.NaN).Dump();  // 0
0d.CompareTo((object)double.NaN).Dump();          // 1

Так что это не проблема.

Теперь из документации Array.Sort(object[]) нет необходимости использовать > , < или == (согласно документации) - просто CompareTo(object) .

Сортирует элементы во всем одномерном массиве, используя реализацию IComparable каждого элемента массива.

Аналогично, Array.Sort(T[]) использует CompareTo(T) .

Сортирует элементы во всем массиве, используя реализацию универсального интерфейса IComparable (Of T) каждого элемента массива.

Посмотрим:

LINQPad (4):

var ar = new double[] {double.NaN, 0, 1, double.NaN};
Array.Sort(ar);
ar.Dump();
// NaN, NaN, 0, 1

LINQPad (3.5):

var ar = new double[] {double.NaN, 0, 1, double.NaN};
Array.Sort(ar);
ar.Dump();
// NaN, 0, NaN, 1

LINQPad (3.5) - ПРИМЕЧАНИЕ. МАССА ОБЪЕКТА, и поведение «ожидается» в контракте CompareTo .

var ar = new object[] {double.NaN, 0d, 1d, double.NaN};
Array.Sort(ar);
ar.Dump();
// NaN, NaN, 0, 1

Хм. В самом деле. В заключение:

У меня НЕ ИДЕЯ - но я подозреваю, что есть некоторая «оптимизация», в результате чего не выполняется CompareTo(double) .

Счастливое кодирование.

В этом вопросе stackoverflow возник интересный вопрос о сортировке двойных массивов с значениями NaN. ОП опубликовал следующий код:

static void Main(string[] args)
{
    double[] someArray = { 4.0, 2.0, double.NaN, 1.0, 5.0, 3.0, double.NaN, 10.0, 9.0, 8.0 };

    foreach (double db in someArray)
    {
        Console.WriteLine(db);
    }

    Array.Sort(someArray);
    Console.WriteLine("\n\n");
    foreach (double db in someArray)
    {
        Console.WriteLine(db);
    }

    Console.ReadLine();
}

Когда вы запускаете это под платформой .NET 3.5, массив сортируется следующим образом:

1,4,NaN,2,3,5,8,9,10,NaN

Когда вы запускаете его под .NET 4.0, массив сортируется несколько более логично:

NaN,NaN,1,2,3,4,5,8,9,10

Я могу понять, почему он будет сортировать странно в .NET 3.5 (потому что NaN не равно, меньше или больше, чем угодно). Я также могу понять, почему он будет сортировать так, как это делается в .NET 4.0. Мой вопрос: почему это изменилось с 3.5 до 4.0? И где находится документация Microsoft для этого изменения?


Не совсем ответ, но, возможно, подсказка ... Вы можете воспроизвести странное поведение 3.5 в 4.0 с помощью этого кода:

void Main()
{
    double[] someArray = { 4.0, 2.0, double.NaN, 1.0, 5.0, 3.0, double.NaN, 10.0, 9.0, 8.0 };
    Array.Sort(someArray, CompareDouble);
    someArray.Dump();
}

int CompareDouble(double a, double b)
{
    if (a > b)
        return 1;
    if (a < b)
        return -1;
    return 0;
}

Здесь оба a > b и a < b возвращают false, если a или b является NaN , поэтому метод CompareDouble возвращает 0, поэтому NaN считается равным всем ... Это дает тот же результат, что и в 3.5:

1,4,NaN,2,3,5,8,9,10,NaN

Это исправление ошибки. Отчет о обратной связи с деталями ошибок приведен здесь . Ответ Microsoft на отчет об ошибке:

Обратите внимание, что эта ошибка влияет на следующее:

  • Array.Sort (), где массив содержит Double.NaN
  • Array.Sort (), где массив содержит Single.NaN
  • любые вызывающие абоненты, например, в List.Sort (), где список содержит Double.NaN

Эта ошибка будет исправлена ​​в следующей крупной версии среды выполнения; до тех пор вы можете обойти это, используя специальный IComparer, который выполняет правильную сортировку. Как упоминалось в обходных комментариях, не используйте Comparer.Default, потому что это специально с помощью процедуры сортировки ярлыков, которая неправильно обрабатывает NaN. Вместо этого вы можете предоставить свой собственный сопоставитель, который обеспечивает эквивалентное сравнение, но не будет специально обложенным.







.net