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

时间:2012-08-24 12:22:13

标签: c# iequatable

我需要在数组中找到 min max 值(不考虑可能的 NaN 值在这个数组中)。

这只适用于double,但这些FindMin和FindMax函数必须使用泛型类型。

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

bool isNaN<T>(T value) where T : IEquatable<T>
{
    return !value.Equals(value);
}

但是Equals正在为true ?? !!

返回double.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);
}

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

您可以找到小测试代码here

2 个答案:

答案 0 :(得分: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));

“浪费”堆栈上的内存非常少,并使用了一个强制转换操作,但是它通用足够来满足任何可以容纳“数字”NaN的类型。

或者,使用float.IsNaN似乎可以用于基本测试,但需要明确预测double(向下转发double.NaN0.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;
    }

但是,这会导致拳击。另请注意,dynamic是必需的,object将不起作用,因此这也会调用DLR(并且还会吞下所有RuntimeBinderException)。

答案 1 :(得分:5)

  

但是对于double.NaN

,Equals返回true

是。它无论泛型如何都是如此:

double x = double.NaN;
Console.WriteLine(x.Equals(x)); // True
Console.WriteLine(x == x); // False

请注意,如果第二行打印为False,则会导致IEquatable<T>.EqualsEquals(object)覆盖,不一致,您必须{{1}违反了Equals(object)的反身性要求。

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

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