对其中包含 NaN 的 Double 数组进行排序

发布于 2024-10-19 05:04:11 字数 745 浏览 6 评论 0原文

这更像是一个“你能解释一下这个”类型的问题吗?

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

引用弗莱的话; “为什么会有那些东西?”

This is more of a 'Can you explain this' type of question than it is anything else.

I came across a problem at work where we were using NaN values in a table, but when the table was sorted, it came out in a very strange, strange manner. I figured NaN was mucking up something so I wrote up a test application to see if this is true. This is what I did.

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();
}

Which gave the result:

Before:

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

After:

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

So yes, the NaN some how made the sorted array to be sorted in a strange way.

To quote Fry; "Why is those things?"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

雨后彩虹 2024-10-26 05:04:11

我相信这是因为

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

对它们的比较失败了,这导致了整个排序的混乱。

I believe that's because

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

so the comparison on them breaks down, and that throws off the entire sort.

旧时浪漫 2024-10-26 05:04:11

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

请参阅错误报告 列表中的错误.Sort() [.NET35] 包含 double.NaN的列表,并在 为什么 .NET 4.0 对此数组进行排序与 .NET 3.5 不同? 我从中删除了链接。

历史沉思

[参见帖子:为什么 .NET 4.0 对该数组的排序方式与 .NET 3.5 不同?,希望能够真正找到关于这个特定问题的更有用的讨论。我也在那里交叉发布了这个回复。]

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

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

无论如何,使用 ><== 的答案解释了为什么这些运算符不起作用无法解释为什么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[]) 文档 -- 没有使用 ><==< /code> (根据文档)- 只是 CompareTo(object)

<块引用>

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

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

<块引用>

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

让我们看看:

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

唔。真的。总之:

我没有任何想法。

快乐编码。

Edit (conclusion. final. end.): This is a bug.

See bug-report Bug in List<double/single>.Sort() [.NET35] in list which contains double.NaN and go give Hans Passant an up-vote at the Why does .NET 4.0 sort this array differently than .NET 3.5? from which I ripped the link.

Historical musings

[See the post: Why does .NET 4.0 sort this array differently than .NET 3.5?, where, hopefully, more useful discussion on this particular issue can be figured out for real. I have cross-posted this response there as well.]

The behavior pointed out in .NET4 by Phil is that defined in CompareTo. See double.CompareTo for .NET4. This is the same behavior as in .NET35 however and should be consistent in both versions, per the method documentation...

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.

In any case, the answers using > and < and == explain why those operators don't work but fail to explain why Array.Sort leads to unexpected output. Here are some of my findings, as meager as they may be.

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

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

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

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

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

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:

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).

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

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

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

Let's see:

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

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.

Happy coding.

层林尽染 2024-10-26 05:04:11

从概念上讲,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);
    }
}

Conceptually NaN is Not a Number, so comparing to a number makes no sense, hence:

a < NaN = false for all a,
a > NaN = false for all a and
NaN != NaN (!)

To solve this you need to write your own comparer that uses IsNaN to make NaNs smaller than (or larger than) all numbers, so that they will all appear at one end of the sort.

Edit: Here's a sample of the Comparison version:

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);
    }
}
自我难过 2024-10-26 05:04:11

实际上,奇怪的排序行为是 .NET 3.5 中的错误造成的。该错误已通过 .NET 4.0 得到解决。

解决此问题的唯一方法是使用您自己的自定义比较器,或升级到 .NET 4.0。请参阅为什么 .NET 4.0 对此数组的排序方式与 .NET 3.5 不同?

Actually, the strange sorting behavior is the result of a bug in .NET 3.5. The bug was addressed with .NET 4.0.

The only way to resolve it is to use your own custom comparer, or upgrade to .NET 4.0. See Why does .NET 4.0 sort this array differently than .NET 3.5?

云淡月浅 2024-10-26 05:04:11

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

since you are using the default sort which is QuickSort algorithm; the implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文