Question

Jon Skeet reports today (source) that :

Math.Max(1f, float.NaN) == NaN
new[] { 1f, float.NaN }.Max() == 1f

Why?

Edit: same issue with double also!

Was it helpful?

Solution

As others have posted, I tweeted one sort of "why" - in that it's using IComparable as documented.

That just leads to another "why" though. In particular:

Console.WriteLine(Math.Max(0, float.NaN));  // Prints NaN
Console.WriteLine(0f.CompareTo(float.NaN)); // Prints 1

The first line suggests that NaN is regarded as being greater than 0. The second line suggests that 0 is regarded as being greater than NaN. (Neither of these can report the result of "this comparison doesn't make sense", of course.)

I have the advantage of seeing all the reply tweets, of course, including these two:

It may seem unusual, but that's the right answer. max() of an array is NaN iff all elements are NaN. See IEEE 754r.

Also, Math.Max uses IEEE 754r total ordering predicate, which specifies relative ordering of NaN vs. others.

OTHER TIPS

He also explained the reason why in this follow-up tweet:

It's because the extension method uses (and is documented to use) the implementation of IComparable, which compares anything as > NaN.

One addition to the (correct) answers stated: Both behave as documented, even if you read the simple explanation only.

Max() extension on this IEnumerable:

Returns the maximum value in a sequence of Single values.

and Math.Max():

[Returns] Parameter val1 or val2, whichever is larger. If val1, val2, or both val1 and val2 are equal to NaN, NaN is returned.

Note that NaN is not a value - so the enumerable Max always returns the largest value. Math.Max returns the greater of two values, or NaN if either or both of them is NaN.

The Math.max method is specifically designed to return NaN if you pass NaN as an argument. Note that this means that it's possible for Math.max(a, b) to return a value that isn't greater than either argument; NaN compared with any operator to any other value yields false.

When using .Max() on the array, the default implementation (I believe) scans over the list looking for the value that compares greater than any other value. Since NaN never compares greater than anything, it won't be chosen by the function.

In short, I think the answer to your question is that Math.Max is weird, whereas the extension method Max is getting it right.

Others have posted the answer that John posted (the extension method uses IComparable which returns anything as > then NaN), and using reflector to look at the implementation of Math.Max shows this

public static double Max(double val1, double val2)
{
    if (val1 > val2)
    {
        return val1;
    }
    if (double.IsNaN(val1))
    {
        return val1;
    }
    return val2;
}

So you can see why they return different results. If you run (1.0 > double.NaN) it will return false.

I suppose that whether 1 or Nan is greater is not defined by any standard, so it is left to the implementation to decide this. Note that all these statements produce false:

        Console.WriteLine("1>Nan {0}]", 1.0 > double.NaN);
        Console.WriteLine("1<Nan {0}]", 1.0 < double.NaN);
        Console.WriteLine("1>=Nan {0}]", 1.0 >= double.NaN);
        Console.WriteLine("1<=Nan {0}]", 1.0 <= double.NaN);

So if Max() is defined as:

if (a<=b) return b else return a;

it will return a if any of the arguments is none.

if (a>b) return a else return b;

And this, also correct implementation of max always return b if any of the arguments is Nan.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top