2011-01-08 19 views
9

Jon Skeet raporty dzisiaj (source), że:Math.max vs Enumerable.Max

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

Dlaczego?

Edycja: ten sam problem z podwójnym również!

+0

Link do miejsca, w którym to zgłasza? –

+0

@matt: jego konto na Twitterze, @jonskeet –

+2

'Maks.' Jest rozszerzeniem dostarczanym przez 'Enumerable' przed' IEnumerable ', nie pochodzi z' Array'. –

Odpowiedz

4

Jak inni napisali, ja wpisów jeden rodzaju „dlaczego” - tym, że używa IComparable jako udokumentowane.

To tylko prowadzi do innego "dlaczego".W szczególności:

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

Pierwszy wiersz wskazuje, że NaN jest traktowane jako większa niż 0. Druga linia sugeruje, że 0 jest uważany jako większa niż NaN. (Żaden z nich może zgłosić wynik „to porównanie nie ma sensu”, oczywiście.)

mam tę przewagę, widząc wszystkie tweety odpowiedź, oczywiście, w tym thesetwo:

Może wydawać się to niezwykłe, ale jest to właściwa odpowiedź. max() tablicy to NaN iff Wszystkie elementy są NaN. Zobacz IEEE 754r.

Ponadto Math.Max ​​używa IEEE 754r całkowitego porządkującego orzecznika, który określa względne porządkowanie NaN w stosunku do innych.

+0

@john: dziękuję john –

+0

pytanie, czy zespół C# powinien zająć się takimi nieoczekiwanymi rezultatami? –

+0

@HPT: Zespół C# nie ma z tym nic wspólnego - to pytanie z biblioteki. Ale zasadniczo podejrzewam, że większość osób (włącznie z mną) byłaby zaskoczona wieloma rzeczami dotyczącymi NaN. –

5

Wyjaśnił także, dlaczego w this obserwacji tweet:

To dlatego zastosowań metodę rozszerzenia (i jest udokumentowane w użyciu) realizacja IComparable, który porównuje wszystko co> Nan.

0

Inni napisali odpowiedź, że Jan pisał (metoda rozszerzenie korzysta IComparable który powraca jako coś> następnie NaN), a przy użyciu reflektor patrzeć na realizację Math.max pokazuje to

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

Możesz więc zobaczyć, dlaczego zwracają różne wyniki. Jeśli uruchomisz (1.0> double.NaN) zwróci false.

1

Metoda Math.max została specjalnie zaprojektowana, aby zwracać NaN, jeśli przekazujesz NaN jako argument. Zauważ, że oznacza to, że Math.max (a, b) może zwrócić wartość, która nie jest większa od żadnego argumentu; NaN w porównaniu z dowolnym operatorem do dowolnej innej wartości daje fałsz.

Podczas używania .Max() na tablicy, domyślna implementacja (jak sądzę) skanuje listę szukając wartości, która porównuje większą niż jakakolwiek inna wartość. Ponieważ NaN nigdy nie porównuje większego niż cokolwiek innego, nie zostanie wybrane przez funkcję.

W skrócie, myślę, że odpowiedź na twoje pytanie jest taka, że ​​Math.Max ​​jest dziwny, podczas gdy metoda rozszerzenia Maxa poprawia to.

2

Jeden z dodanych do (poprawnych) odpowiedzi: Oba zachowują się jak udokumentowane, nawet jeśli czytasz tylko proste wyjaśnienie.

Max() rozszerzenie na ten IEnumerable:

Zwraca maksymalną wartość w sekwencji pojedynczych wartości.

i Math.max():

[Zwraca] wart1 Parametr lub wart2, która wartość jest większa. Jeśli val1, val2 lub zarówno val1, jak i val2 są równe NaN, zwracane jest 01NNaN.

Zauważ, że NaN nie jest wartością - więc przeliczalny Max zawsze zwraca największą wartość. Math.Max ​​zwraca większą z dwóch wartości lub NaN, jeśli jedna lub obie z nich są NaN.

0

Przypuszczam, że to, czy 1 lub Nan jest większe, nie jest zdefiniowane przez żaden wzorzec, więc decyzję o tym należy pozostawić wdrożeniu. Należy pamiętać, że wszystkie te oświadczenia produkować fałszywe:

 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); 

Więc jeśli Max() jest zdefiniowany jako:

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

to zwróci, jeśli któryś z argumentów jest żaden.

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

I to też prawidłowe wdrożenie max zawsze powrócić b jeśli któryś z argumentów jest Nan.