为什么.NET 4.0以与.NET 3.5不同的方式对此数组进行sorting?
这个stackoverflow问题提出了一个有趣的问题关于sorting与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框架下运行时,数组的sorting如下:
1,4,NaN,2,3,5,8,9,10,NaN
当你在.NET 4.0下运行它时,数组在逻辑上被sorting得更多一些:
NaN,NaN,1,2,3,4,5,8,9,10
我可以理解为什么它会在.NET 3.5中奇怪地sorting(因为NaN不等于,小于或大于任何东西)。 我也可以理解为什么它会按照.NET 4.0的方式进行sorting。 我的问题是,为什么这个从3.5变成4.0? 微软的这个改变文件在哪里?
这是一个错误修复。 有错误细节的反馈报告在这里 。 微软对错误报告的回应:
请注意,这个错误会影响以下内容:
- Array.Sort(),其中数组包含Double.NaN
- Array.Sort(),其中数组包含Single.NaN
- 上面的任何调用者,例如List.Sort(),其中列表包含Double.NaN
该错误将在下一个主要版本的运行时中得到解决。 直到那时你可以通过使用自定义的IComparer来做到正确的sorting。 正如在变通办法评论中提到的,不要使用Comparer.Default,因为这是特殊的,不适合使用NaN的快捷方式sorting例程。 相反,你可以提供你自己的比较器,提供了一个等效的比较,但不会是特殊的。
不是一个真正的答案,但也许是一个线索…你可以用这个代码在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是NaN
,那么a > b
和a < b
返回false,所以CompareDouble
方法返回0,所以NaN
被认为等于一切…这给出了与3.5中相同的结果:
1,4,NaN,2,3,5,8,9,10,NaN
我没有用于validation这一点的.NET 3.5运行时代码,但是我认为他们修复了默认比较器中的一个错误,使其与文档一致 。
根据该文件, Double.Compare
将NaN
等同于PositiveInfinity
和NegativeInfinity
,并且小于任何其他值。
该文档是相同的.NET 3.5和.NET 4.0,所以我不得不认为这是一个错误修复,使代码工作logging。
编辑:
在阅读链接问题中的注释之后,我不得不认为问题不在Double.Compare
,而是在Array.Sort
(这是List.Sort
依赖的)用来比较Double
值的任何方法中。 不知何故,我不认为这真的是Double.Compare
。
[这是我从另一个post中无耻地撕下的回应。 这将是很好的是有人探讨了这一点 – double.CompareTo
和double.CompareTo(double)
是定义良好,如下所示,所以我怀疑有一些Array.Sort
魔术发生在特定types。
Array.Sort(double[])
: 似乎没有像预期的那样使用CompareTo(double[])
,这很可能是一个bug – 注意Array.Sort(object [])和Array.Sort (double []) 。 我希望澄清/更正以下内容。
首先, double.CompareTo(T)
方法文档 – 这个sorting是根据文档定义的 :
小于零 :此实例小于值。 – 或者 – 此实例不是一个数字(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)
。
使用Array的每个元素的
IComparable
实现对整个一维Array中的元素进行sorting。
同样, Array.Sort(T[])
使用CompareTo(T)
。
使用Array的每个元素的IComparable(Of T)通用接口实现对整个Array中的元素进行sorting。
让我们来看看:
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)
不被调用。
快乐的编码。