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文档在哪里?
答案 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 < b
和NaN
都会返回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.Compare
将NaN
视为等于PositiveInfinity
和NegativeInfinity
,并且小于任何其他值。
.NET 3.5和.NET 4.0的文档是相同的,所以我不得不认为这是一个错误修复,可以使代码按照文档的形式工作。
编辑:
在阅读链接问题中的评论后,我不得不认为问题不在Double.Compare
中,而在于Array.Sort
(List.Sort
取决于Double
的方法)用于比较Double.Compare
值。不知何故,我认为这不是{{1}}。
答案 3 :(得分:2)
[这是我的反应无可耻地从另一篇文章中撕掉。如果有人进一步探讨这一点会很好 - double.CompareTo
和double.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)
没有被调用。
快乐的编码。