How to test for NaN with generics (or why NaN.Equals(NaN) == true)?
-
28-06-2021 - |
Question
I need to find min and max values in an array (not taking into for possible NaN values in this array).
This would be easy only working with double
, but these FindMin and FindMax functions have to work with generics types.
I have tried to test for generic NaNs in this way:
bool isNaN<T>(T value) where T : IEquatable<T>
{
return !value.Equals(value);
}
but Equals
is returning true
for double.NaN
??!!
I have a workaround like this for now:
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);
}
My question is more about understanding why first solution did not work, is this a bug ?
You can find small test code here
Solution
but Equals is returning true for double.NaN
Yes. And it does regardless of generics:
double x = double.NaN;
Console.WriteLine(x.Equals(x)); // True
Console.WriteLine(x == x); // False
Note that if the second line printed False, that would either make the IEquatable<T>.Equals
inconsistent with the Equals(object)
override, or you'd have to make Equals(object)
violate the reflexivity requirement of object.Equals(object)
.
Basically this sort of thing is nasty whatever you do with it.
Given that you're trying to find max/min values, you might want to try using IComparable<T>
instead of IEquatable<T>
- that may well let you detect NaN in other ways. (I don't have time to check right now.)
OTHER TIPS
I would simply use double.IsNaN
and let the compiler implicitly cast float
members where required:
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));
"Wastes" a very small amount of memory on the stack and uses a cast op call, but hey ho it is generic enough to cater for any type that can hold a "numeric" NaN.
Alternatively, using float.IsNaN
appears to work with rudimentary testing, but requires an explicit downcast of a double
(downcasting double.NaN
and 0.0 / 0.0
appears to work, as in, it reports NaN
correctly).
You cannot generically constrain on a subset of value types with any cleanliness (or at all, I'm not 100% certain) so I wouldn't bother chasing the <T>
route personally.
If you want a general solution that means the callee doesn't need to care what is passed in, you can do the following:
static bool IsNaN(dynamic d)
{
float dub;
try
{
dub = (float)d;
return float.IsNaN(dub);
}
catch (RuntimeBinderException)
{
}
return false;
}
However, this incurs boxing. Note as well that dynamic
is required and object
will not work, so this also invokes the DLR (and also swallows all RuntimeBinderException
s).