用NaN对双打数组进行排序

时间:2011-02-25 23:02:29

标签: c# sorting .net-3.5 nan

这更像是一个'你能解释这个'的问题而不是其他任何问题。

我在工作中遇到了一个问题,我们在表中使用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有些如何使排序的数组以奇怪的方式排序。

引用弗莱; “为什么这些东西?”

5 个答案:

答案 0 :(得分:10)

我相信那是因为

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

因此对它们的比较会中断,这会导致整个排序。

答案 1 :(得分:6)

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

请参阅错误报告Bug in List<double/single>.Sort() [.NET35] in list which contains double.NaN,然后在Why does .NET 4.0 sort this array differently than .NET 3.5?上向Hans Passant投票,我从中撕开链接。

历史沉思

[请参阅帖子:Why does .NET 4.0 sort this array differently than .NET 3.5?,希望有关此特定问题的更有用的讨论可以在实际中找到。我也在那里交叉发布了这个回复。]

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

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

在任何情况下,使用><以及==的答案都解释了为什么这些运算符不起作用无法解释< / strong>为什么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.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

嗯。真。总之:

我没有想法。

快乐的编码。

答案 2 :(得分: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);
    }
}

答案 3 :(得分:0)

实际上,奇怪的排序行为是.NET 3.5中的一个错误的结果。这个bug是用.NET 4.0解决的。

解决它的唯一方法是使用自己的自定义比较器,或升级到.NET 4.0。见Why does .NET 4.0 sort this array differently than .NET 3.5?

答案 4 :(得分:-1)

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