如何用泛型测试NaN(或为什么NaN.Equals(NaN)== true)?

Cit*_*ane 3 c# iequatable

我需要在数组中找到最小值最大值(不考虑此数组中可能的NaN值).

这很容易使用double,但是这些FindMin和FindMax函数必须使用泛型类型.

我试图用这种方式测试通用 NaN:

bool isNaN<T>(T value) where T : IEquatable<T>
{
    return !value.Equals(value);
}
Run Code Online (Sandbox Code Playgroud)

但是Equals回来truedouble.NaN!! !!

我现在有这样的解决方法:

bool isNaN<T>(T value) where T : IEquatable<T>
{
    var d = value as double?;
    if (d.HasValue) { return double.IsNaN(d.Value); }

    return !value.Equals(value);
}
Run Code Online (Sandbox Code Playgroud)

我的问题更多的是理解为什么第一个解决方案不起作用,这是一个错误吗?

你可以在这里找到小的测试代码

Ada*_*rth 7

我只是使用double.IsNaN并让编译器float在需要的地方隐式地转换成员:

float myFloat = float.NaN; // or 0.0f / 0.0f;
double myDouble = double.NaN; // or 0.0 / 0.0;

Console.WriteLine(double.IsNaN(myFloat));
Console.WriteLine(double.IsNaN(myDouble));
Run Code Online (Sandbox Code Playgroud)

"浪费"是堆栈中非常少量的内存,并使用强制转换操作,但是它很通用,足以满足任何可以容纳"数字"NaN的类型.

或者,使用float.IsNaN似乎与初步测试一起工作,但需要明确的向下转发double(向下转换double.NaN并且0.0 / 0.0似乎有效,因为它NaN正确报告).

你不能一般地限制任何清洁度的价值类型的子集(或者根本不是100%确定)所以我不打扰<T>个人追逐路线.


如果您想要一个通用解决方案,这意味着被调用者不需要关心传入的内容,您可以执行以下操作:

    static bool IsNaN(dynamic d)
    {
        float dub;

        try
        {
            dub = (float)d;
            return float.IsNaN(dub);
        }
        catch (RuntimeBinderException)
        {
        }

        return false;
    }
Run Code Online (Sandbox Code Playgroud)

然而,这导致拳击.注意这dynamic是必需的并且object不起作用,因此这也调用DLR(并且还吞下所有RuntimeBinderExceptions).


Jon*_*eet 5

但是Equals正在为double.NaN返回true

是.它不管泛型如何:

double x = double.NaN;
Console.WriteLine(x.Equals(x)); // True
Console.WriteLine(x == x); // False
Run Code Online (Sandbox Code Playgroud)

请注意,如果第二行打印为False,则会导致IEquatable<T>.EqualsEquals(object)覆盖不一致,或者您必须Equals(object)违反反射性要求object.Equals(object).

无论你用它做什么,基本上这种事都很讨厌.

鉴于您正在尝试查找最大值/最小值,您可能希望尝试使用IComparable<T>而不是IEquatable<T>- 这可能会让您以其他方式检测NaN.(我现在没时间检查.)