为什么.NET 4.0对此数组的排序与.NET 3.5不同?

时间:2011-02-25 23:33:14

标签: c# .net

This stackoverflow question提出了一个关于使用NaN值对双数组进行排序的有趣问题。 OP发布了以下代码:

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文档在哪里?

4 个答案:

答案 0 :(得分:38)

这是一个错误修复。包含错误详细信息的反馈报告is here。微软对错误报告的回应:

  

请注意,此错误会影响以下内容:

     
      
  • Array.Sort(),其中数组包含Double.NaN
  •   
  • Array.Sort(),其中数组包含Single.NaN
  •   
  • 上面的任何调用者,例如List.Sort(),其中list包含Double.NaN
  •   
     

此错误将在运行时的下一个主要版本中修复;在此之前,您可以使用执行正确排序的自定义IComparer来解决此问题。正如变通方法注释中所提到的,不要使用Comparer.Default,因为这是特殊的,带有快捷排序例程,无法正确处理NaN。相反,您可以提供自己的比较器,提供相同的比较,但不会是特殊的。

答案 1 :(得分:6)

不是真正的答案,但也许是一个线索......您可以使用以下代码重现4.0中奇怪的3.5行为:

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,则a < bNaN都会返回false,因此CompareDouble方法返回0,因此NaN被视为相等对所有事情......这给出了与3.5中相同的结果:

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

答案 2 :(得分:2)

我没有.NET 3.5运行时的代码来验证这一点,但我认为他们修复了double的默认比较器中的错误,使其与the documentation一致

根据该文件,Double.CompareNaN视为等于PositiveInfinityNegativeInfinity,并且小于任何其他值。

.NET 3.5和.NET 4.0的文档是相同的,所以我不得不认为这是一个错误修复,可以使代码按照文档的形式工作。

编辑:

在阅读链接问题中的评论后,我不得不认为问题不在Double.Compare中,而在于Array.SortList.Sort取决于Double的方法)用于比较Double.Compare值。不知何故,我认为这不是{{1}}。

答案 3 :(得分:2)

[这是我的反应无可耻地从另一篇文章中撕掉。如果有人进一步探讨这一点会很好 - double.CompareTodouble.CompareTo(double)定义明确,如下所示,所以我怀疑特定类型会发生一些Array.Sort魔法。] < / p>

Array.Sort(double[])似乎没有按预期使用CompareTo(double[]),这很可能是一个错误 - 请注意Array.Sort(object [])和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

嗯。真。总之:

我没有想法 - 但我怀疑有一些“优化”导致CompareTo(double)没有被调用。

快乐的编码。