Math.Max ​​против Enumerable.Max - PullRequest
9 голосов
/ 08 января 2011

Джон Скит сообщает сегодня ( источник ), что:

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

Почему?

Редактировать: та же проблема с двойной тоже!

Ответы [ 6 ]

5 голосов
/ 08 января 2011

Он также объяснил причину, по которой в этом последующем твите:

Это потому, что метод расширения использует (и задокументировано для использования) реализацию IComparable, котораясравнивает что угодно как> NaN.

4 голосов
/ 08 января 2011

Как уже сообщали другие, я написал в Твиттере один своего рода "почему" - в этом он использует IComparable, как задокументировано.

Это просто приводит к другому "почему", хотя. В частности:

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

Первая строка указывает на то, что NaN считается больше 0. Вторая строка указывает на то, что 0 считается больше, чем NaN. (Конечно, ни один из них не может сообщить о результате «это сравнение не имеет смысла».)

Я, конечно, имею преимущество видеть все ответные твиты, включая эти два :

Это может показаться необычным, но это правильный ответ. max () массива - NaN, если все элементы - NaN. См. IEEE 754r.

Кроме того, Math.Max ​​использует предикат общего порядка упорядочения IEEE 754r, который определяет относительное упорядочение NaN по сравнению с другими.

2 голосов
/ 08 января 2011

Было добавлено одно дополнение к (правильным) ответам: оба ведут себя как задокументировано, даже если вы читаете только простое объяснение.

Расширение Max () для этого IEnumerable:

Возвращает максимальное значение в последовательности значений Single.

и Math.Max ​​():

[Возвращает] Параметр val1 или val2, в зависимости от того, что больше.Если val1, val2 или оба val1 и val2 равны NaN, возвращается NaN.

Обратите внимание, что NaN не является значением, поэтому перечисляемый Max всегда возвращает наибольшее значение * 1016.*.Math.Max ​​возвращает большее из двух значений, или NaN, если одно или оба из них имеют значение NaN.

1 голос
/ 08 января 2011

Метод Math.max специально разработан для возврата NaN, если вы передаете NaN в качестве аргумента.Обратите внимание, что это означает, что Math.max (a, b) может вернуть значение, не превышающее ни один из аргументов;NaN по сравнению с любым оператором для любого другого значения приводит к значению false.

При использовании .Max () для массива реализация по умолчанию (я считаю) просматривает список в поисках значения, которое сравнивается с любым другим значением,Поскольку NaN никогда не сравнивает больше, чем что-либо, он не будет выбран функцией.

Короче говоря, я думаю, что ответ на ваш вопрос заключается в том, что Math.Max ​​странный, тогда как метод расширения Max его получаетправый.

0 голосов
/ 08 января 2011

Я полагаю, что значение 1 или Nan больше не определяется ни одним стандартом, поэтому решение остается за реализацией. Обратите внимание, что все эти утверждения выдают 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);

То есть, если Max() определяется как:

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

он вернет a, если ни один из аргументов не равен ниому.

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

И это, также правильная реализация max всегда возвращает b, если любой из аргументов равен Nan.

0 голосов
/ 08 января 2011

Другие опубликовали ответ, который отправил Джон (метод расширения использует IComparable, который возвращает что-либо как> затем NaN), а использование отражателя для просмотра реализации Math. Max показывает это

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

Итак, вы можете видеть, почему они дают разные результаты. Если вы запустите (1.0> double.NaN), он вернет false.

...