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

时间:2022-10-21 06:36:45

This * question raised an interesting question about sorting double arrays with NaN values. The OP posted the following code:

这个*问题提出了一个有关使用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();
}

When you run this under the .NET 3.5 framework, the array is sorted as follows:

在.NET 3.5框架下运行时,数组按如下方式排序:

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

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

When you run it under .NET 4.0, the array is sorted somewhat more logically:

在.NET 4.0下运行它时,数组的逻辑排序更为一些:

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

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

I can understand why it would sort weirdly in .NET 3.5 (because NaN is not equal to, less than, or greater than, anything). I can also understand why it would sort the way it does in .NET 4.0. My question is, why did this change from 3.5 to 4.0? And where is the Microsoft documentation for this change?

我可以理解为什么它会在.NET 3.5中奇怪地排序(因为NaN不等于,小于或大于任何东西)。我也可以理解为什么它会像在.NET 4.0中那样排序。我的问题是,为什么这个从3.5变为4.0?这个变化的Microsoft文档在哪里?

4 个解决方案

#1


37  

It's a bug fix. The feedback report with the bug details is here. Microsoft's response to the bug report:

这是一个bug修复。带有错误详细信息的反馈报告就在这里。微软对错误报告的回应:

Note that this bug affects the following:

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

  • Array.Sort(), where the array contains Double.NaN
  • Array.Sort(),其中数组包含Double.NaN
  • Array.Sort(), where the array contains Single.NaN
  • Array.Sort(),其中数组包含Single.NaN
  • any callers of above, for example on List.Sort(), where list contains Double.NaN
  • 任何上面的调用者,例如在List.Sort()上,其中list包含Double.NaN

This bug will be fixed in the next major version of the runtime; until then you can work around this by using a custom IComparer that does the correct sorting. As mentioned in the workaround comments, don't use Comparer.Default, because this is special-cased with a shortcut sort routine that doesn't handle NaN correctly. Instead, you can provide your own comparer that provides an equivalent comparision, but won't be special-cased.

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

#2


6  

Not really an answer, but perhaps a clue... You can reproduce the weird 3.5 behavior in 4.0 with this code:

不是真正的答案,但也许是一个线索......您可以使用以下代码重现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;
}

Here, both a > b and a < b return false if a or b is NaN, so the CompareDouble method returns 0, so NaN is considered equal to everything... This gives the same result as in 3.5:

这里,如果a或b是NaN,a> b和a 都返回false,因此comparedouble方法返回0,因此认为nan等于所有...这得到与3.5中相同的结果:

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

#3


2  

I don't have the code for the .NET 3.5 runtime to verify this, but I'm thinking they fixed a bug in the default comparer for double to bring it into line with the documentation.

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

According to that document, Double.Compare treats NaN as equal to PositiveInfinity and NegativeInfinity, and less than any other value.

根据该文档,Double.Compare将NaN视为等于PositiveInfinity和NegativeInfinity,并且小于任何其他值。

The documentation is the same for .NET 3.5 and .NET 4.0, so I have to think that this was a bug fix to make the code work as documented.

.NET 3.5和.NET 4.0的文档是相同的,所以我必须认为这是一个错误修复,使代码按照文档记录的方式工作。

EDIT:

编辑:

After reading the comments in the linked question, I have to think that the problem wasn't in Double.Compare, but rather in whatever method Array.Sort (which is what List.Sort depends on) uses to compare Double values. Somehow, I don't think it's really Double.Compare.

在阅读链接问题中的注释后,我不得不认为问题不在Double.Compare中,而是在Array.Sort(List.Sort所依赖的)用于比较Double值的任何方法中。不知何故,我不认为它真的是Double.Compare。

#4


2  

[This is my response shameless ripped from the other post. It would be nice is someone explored this further -- double.CompareTo and double.CompareTo(double) are well-defined, as indicated below so I suspect there is some Array.Sort magic happening for the specific type.]

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

Array.Sort(double[]): doesn't seem to be using CompareTo(double[]) as expected and this may very well be a bug -- note the difference in Array.Sort(object[]) and Array.Sort(double[]) below. I would love clarification/corrections on the following.

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

First, the double.CompareTo(T) method documentation -- this ordering is well-defined according to the documentation:

首先,double.CompareTo(T)方法文档 - 根据文档很好地定义了这个顺序:

Less than zero: This instance is less than value. -or- This instance is not a number (NaN) and value is a number.

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

Zero: This instance is equal to value. -or- Both this instance and value are not a number (NaN), PositiveInfinity, or NegativeInfinity.

零:此实例等于值。 - 或 - 此实例和值都不是数字(NaN),PositiveInfinity或NegativeInfinity。

Greater than zero: This instance is greater than value. -or- This instance is a number and value is not a number (NaN).

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

In LINQPad (3.5 and 4, both have same results):

在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

Using CompareTo(object) has the same results:

使用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

So that's not the problem.

所以这不是问题。

Now, from the Array.Sort(object[]) documentation -- there is no use of >, < or == (according to the documentation) -- just CompareTo(object).

现在,从Array.Sort(object [])文档 - 没有使用>, <或==(根据文档) - 只是compareto(对象)。< p>

Sorts the elements in an entire one-dimensional Array using the IComparable implementation of each element of the Array.

使用Array的每个元素的IComparable实现对整个一维Array中的元素进行排序。

Likewise, Array.Sort(T[]) uses CompareTo(T).

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

Sorts the elements in an entire Array using the IComparable(Of T) generic interface implementation of each element of the Array.

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

Let's see:

让我们来看看:

LINQPad (4):

LINQPad(4):

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

LINQPad (3.5):

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) -- NOTE THE ARRAY IS OF OBJECT and the behavior is "expected" per the CompareTo contract.

LINQPad(3.5) - 注意阵列是对象,并且根据CompareTo合同,行为是“预期的”。

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

Hmm. Really. In conclusion:

嗯。真。结论是:

I HAVE NO IDEA -- but I suspect there is some "optimization" resulting in CompareTo(double) not being invoked.

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

Happy coding.

快乐的编码。

#1


37  

It's a bug fix. The feedback report with the bug details is here. Microsoft's response to the bug report:

这是一个bug修复。带有错误详细信息的反馈报告就在这里。微软对错误报告的回应:

Note that this bug affects the following:

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

  • Array.Sort(), where the array contains Double.NaN
  • Array.Sort(),其中数组包含Double.NaN
  • Array.Sort(), where the array contains Single.NaN
  • Array.Sort(),其中数组包含Single.NaN
  • any callers of above, for example on List.Sort(), where list contains Double.NaN
  • 任何上面的调用者,例如在List.Sort()上,其中list包含Double.NaN

This bug will be fixed in the next major version of the runtime; until then you can work around this by using a custom IComparer that does the correct sorting. As mentioned in the workaround comments, don't use Comparer.Default, because this is special-cased with a shortcut sort routine that doesn't handle NaN correctly. Instead, you can provide your own comparer that provides an equivalent comparision, but won't be special-cased.

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

#2


6  

Not really an answer, but perhaps a clue... You can reproduce the weird 3.5 behavior in 4.0 with this code:

不是真正的答案,但也许是一个线索......您可以使用以下代码重现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;
}

Here, both a > b and a < b return false if a or b is NaN, so the CompareDouble method returns 0, so NaN is considered equal to everything... This gives the same result as in 3.5:

这里,如果a或b是NaN,a> b和a 都返回false,因此comparedouble方法返回0,因此认为nan等于所有...这得到与3.5中相同的结果:

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

#3


2  

I don't have the code for the .NET 3.5 runtime to verify this, but I'm thinking they fixed a bug in the default comparer for double to bring it into line with the documentation.

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

According to that document, Double.Compare treats NaN as equal to PositiveInfinity and NegativeInfinity, and less than any other value.

根据该文档,Double.Compare将NaN视为等于PositiveInfinity和NegativeInfinity,并且小于任何其他值。

The documentation is the same for .NET 3.5 and .NET 4.0, so I have to think that this was a bug fix to make the code work as documented.

.NET 3.5和.NET 4.0的文档是相同的,所以我必须认为这是一个错误修复,使代码按照文档记录的方式工作。

EDIT:

编辑:

After reading the comments in the linked question, I have to think that the problem wasn't in Double.Compare, but rather in whatever method Array.Sort (which is what List.Sort depends on) uses to compare Double values. Somehow, I don't think it's really Double.Compare.

在阅读链接问题中的注释后,我不得不认为问题不在Double.Compare中,而是在Array.Sort(List.Sort所依赖的)用于比较Double值的任何方法中。不知何故,我不认为它真的是Double.Compare。

#4


2  

[This is my response shameless ripped from the other post. It would be nice is someone explored this further -- double.CompareTo and double.CompareTo(double) are well-defined, as indicated below so I suspect there is some Array.Sort magic happening for the specific type.]

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

Array.Sort(double[]): doesn't seem to be using CompareTo(double[]) as expected and this may very well be a bug -- note the difference in Array.Sort(object[]) and Array.Sort(double[]) below. I would love clarification/corrections on the following.

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

First, the double.CompareTo(T) method documentation -- this ordering is well-defined according to the documentation:

首先,double.CompareTo(T)方法文档 - 根据文档很好地定义了这个顺序:

Less than zero: This instance is less than value. -or- This instance is not a number (NaN) and value is a number.

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

Zero: This instance is equal to value. -or- Both this instance and value are not a number (NaN), PositiveInfinity, or NegativeInfinity.

零:此实例等于值。 - 或 - 此实例和值都不是数字(NaN),PositiveInfinity或NegativeInfinity。

Greater than zero: This instance is greater than value. -or- This instance is a number and value is not a number (NaN).

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

In LINQPad (3.5 and 4, both have same results):

在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

Using CompareTo(object) has the same results:

使用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

So that's not the problem.

所以这不是问题。

Now, from the Array.Sort(object[]) documentation -- there is no use of >, < or == (according to the documentation) -- just CompareTo(object).

现在,从Array.Sort(object [])文档 - 没有使用>, <或==(根据文档) - 只是compareto(对象)。< p>

Sorts the elements in an entire one-dimensional Array using the IComparable implementation of each element of the Array.

使用Array的每个元素的IComparable实现对整个一维Array中的元素进行排序。

Likewise, Array.Sort(T[]) uses CompareTo(T).

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

Sorts the elements in an entire Array using the IComparable(Of T) generic interface implementation of each element of the Array.

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

Let's see:

让我们来看看:

LINQPad (4):

LINQPad(4):

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

LINQPad (3.5):

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) -- NOTE THE ARRAY IS OF OBJECT and the behavior is "expected" per the CompareTo contract.

LINQPad(3.5) - 注意阵列是对象,并且根据CompareTo合同,行为是“预期的”。

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

Hmm. Really. In conclusion:

嗯。真。结论是:

I HAVE NO IDEA -- but I suspect there is some "optimization" resulting in CompareTo(double) not being invoked.

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

Happy coding.

快乐的编码。