16

这更像是一个“你能解释一下这个”类型的问题,而不是其他任何问题。

我在工作中遇到了一个问题,我们在表中使用 NaN 值,但是当对表进行排序时,它以一种非常奇怪的方式出现。我认为 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();
}

结果是:

前:

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

后:

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

所以是的,NaN 是如何使排序后的数组以一种奇怪的方式排序的。

引用弗莱的话;“为什么是那些东西?”

4

5 回答 5

10

我相信那是因为

a < NaN == false
a > NaN == false
a == NaN == false

所以对它们的比较就失效了,这就打乱了整个排序。

于 2011-02-25T23:05:48.767 回答
6

编辑(结论。最终。结束。):这一个错误。

请参阅包含 double.NaN 的列表中 List<double/single>.Sort() [.NET35] 中的错误报告错误,并在为什么 .NET 4.0 对这个数组的排序不同于 .NET 3.5?我从中撕掉了链接。

历史沉思

[参见帖子:为什么 .NET 4.0 对这个数组的排序与 .NET 3.5 不同?,希望能真正找到关于这个特定问题的更有用的讨论。我也在那里交叉发布了这个回复。]

Phil 在 .NET4 中指出的行为是在 CompareTo 中定义的。请参阅.NET4的 double.CompareTo。这与 .NET35 中的行为相同,但是根据方法文档,两个版本中应该保持一致...

Array.Sort(double[]):似乎没有CompareTo(double[])按预期使用,这很可能是一个错误——请注意下面 Array.Sort(object[]) 和 Array.Sort(double[]) 的区别。我希望对以下内容进行澄清/更正。

无论如何,使用>and的答案<==解释为什么这些运算符不起作用未能解释为什么Array.Sort会导致意外输出。以下是我的一些发现,尽管它们可能微不足道。

首先,double.CompareTo(T)方法文档——这个顺序是根据文档明确定义的

小于零:此实例小于值。- 或 - 此实例不是数字 (NaN),值是数字。

:此实例等于值。- 或 - 此实例和值都不是数字 (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的每个元素的实现对整个一维 Array 中的元素进行排序。

同样,Array.Sort(T[])使用CompareTo(T).

使用 Array 的每个元素的 IComparable(Of T) 通用接口实现对整个 Array 中的元素进行排序。

让我们来看看:

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

唔。真的。综上所述:

我不知道。

快乐编码。

于 2011-02-25T23:22:16.613 回答
1

从概念上讲,NaN 不是数字,因此与数字进行比较是没有意义的,因此:

a < NaN = false for all a,
a > NaN = false for all a and
NaN != NaN (!)

要解决这个问题,您需要编写自己的比较器,该比较器使用 IsNaN 使 NaN 小于(或大于)所有数字,以便它们都出现在排序的一端。

编辑:这是比较版本的示例:

class Program
{
    private static int NaNComparison(double first, double second)
    {
        if (double.IsNaN(first))
        {
            if (double.IsNaN(second)) // Throws an argument exception if we don't handle both being NaN
                return 0;
            else
                return -1;
        }
        if (double.IsNaN(second))
            return 1;

        if (first == second)
            return 0;
        return first < second ? -1 : 1;
    }

    static void Main(string[] args)
    {
        var doubles = new[] { double.NaN, 2.0, 3.0, 1.0, double.NaN };

        Array.Sort(doubles, NaNComparison);
    }
}
于 2011-02-25T23:11:44.583 回答
0

实际上,奇怪的排序行为是 .NET 3.5 中的一个错误的结果。该错误已在 .NET 4.0 中得到解决。

解决它的唯一方法是使用您自己的自定义比较器,或升级到 .NET 4.0。请参阅为什么 .NET 4.0 对这个数组的排序与 .NET 3.5 不同?

于 2011-02-26T03:16:23.047 回答
-1

因为您使用的是默认排序,即快速排序算法;实现执行不稳定的排序;也就是说,如果两个元素相等,它们的顺序可能不会被保留

于 2011-02-25T23:09:53.243 回答