9

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!

Community
  • 1
  • 1

6 Answers6

5

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.

adrianbanks
  • 81,306
  • 22
  • 176
  • 206
4

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.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • the question is should C# team take care of this kind of unexpected results? –  Jan 08 '11 at 20:44
  • @HPT: The C# team have nothing to do with it - it's a library question. But fundamentally I suspect that most people (myself included) would be surprised at any number of things about NaN. – Jon Skeet Jan 08 '11 at 20:46
  • @john: I made a mistake, I mean .Net team. –  Jan 08 '11 at 20:55
  • @HPT: Right. But I still think it's just a matter of NaN having fundamentally unexpected behaviour - which no doubt makes sense if you study everything in detail, but looks odd when you only consider some individual cases. And don't forget that the .NET team isn't responsible for IEEE 754 itself :) – Jon Skeet Jan 08 '11 at 21:07
2

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.

Philip Rieck
  • 32,368
  • 11
  • 87
  • 99
1

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.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
0

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.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
0

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.

codymanix
  • 28,510
  • 21
  • 92
  • 151