Почему Release/Debug имеет другой результат для std::min?

Вот тестовая программа:

void testFunc()
{
    double maxValue = DBL_MAX;
    double slope = std::numeric_limits<double>::quiet_NaN();

    std::cout << "slope is " << slope << std::endl;
    std::cout << "maxThreshold is " << maxValue << std::endl;
    std::cout << "the_min is " << std::min( slope, maxValue) << std::endl;
    std::cout << "the_min is " << std::min( DBL_MAX, std::numeric_limits<double>::quiet_NaN()) << std::endl;
}

int main( int argc, char* argv[] )
{
    testFunc();
    return 0;
}

В Debug я получаю:

slope is nan
maxThreshold is 1.79769e+308
the_min is nan
the_min is 1.79769e+308

В Release я получаю:

slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan

Почему я получаю другой результат в Release, чем Debug?

Я уже проверил сообщение "Переполнение стека" Использование функций min и max в C++, и в нем не упоминаются различия в Release/Debug.

Я использую Visual Studio 2015.

3 ответа

Решение

Понял:

Вот реализация, используемая VS в режиме отладки (с _Pred являющийся DEBUG_LT, LT для нижнего уровня):

template<class _Pr,
    class _Ty1,
    class _Ty2> inline
    _CONST_FUN bool _Debug_lt_pred(_Pr _Pred,
        _Ty1&& _Left, _Ty2&& _Right,
        _Dbfile_t _File, _Dbline_t _Line)
    {   // test if _Pred(_Left, _Right) and _Pred is strict weak ordering
    return (!_Pred(_Left, _Right)
        ? false
        : _Pred(_Right, _Left)
            ? (_DEBUG_ERROR2("invalid comparator", _File, _Line), true)
            : true);
    }

Что эквивалентно (более читабельно):

    if (!_Pred(_Left, _Right))
    {
        return false;
    }
    else
    {
        if ( _Pred(_Right, _Left) )
        {
            assert( false );
            return true;
        }
        else
        {
            return true;
        }
    }

Что опять-таки эквивалентно (!_Pred(_Left, _Right)), Записывается как макрос, становится #define _DEBUG_LT(x, y) !((y) < (x)) (т.е.: НЕ справа <слева).

Реализация релиза на самом деле является макросом #define _DEBUG_LT(x, y) ((x) < (y)) (т.е.: слева <справа).

Итак, отладка (!(y<x)) и релиз (x<y) реализации определенно не одинаковы, и они ведут себя по-разному, если один параметр является NaN...! Не спрашивайте, почему они это сделали....

В IEEE 754 сравнение NAN с чем-либо всегда даст falseнезависимо от того, что это.

slope > 0; // false
slope < 0; // false
slope == 0; // false

И, что более важно для вас

slope < DBL_MAX; // false
DBL_MAX < slope; // false

Так что кажется, что компилятор переупорядочивает параметры / использует > или же <= вместо <и вот почему вы получаете разные результаты.

Например, эти функции могут быть описаны как таковые

Релиз:

double const& min(double const& l, double const r) {
    return l <= r ? l : r;
}

Debug:

double const& min(double const& l, double const& r) {
    return r < l ? r : l;
}

Требования (LessThanComparable) по std::min кроме того, они имеют то же значение арифметически. Но они дают разные результаты, когда вы используете их с NaN.

Вы не указали, какой формат представления с плавающей запятой использует ваш процессор. Но, поскольку вы используете Visual Studio, я предполагаю, что вы используете Windows, а затем я предполагаю, что ваш процессор использует представление IEEE 754.

В IEEE 754 NaN неупорядочен по каждому числу. Это означает, что (NaN < f) == false а также (f < NaN) == false для любого значения f, Педантически это означает, что числа с плавающей запятой, которые поддерживают NaN, не соответствуют требованиям LessThanComparable, которое является требованием для std::min, Практически std::min ведет себя так, как указано в стандарте, если ни один из аргументов не является NaN.

Поскольку одним из аргументов в вашем коде является NaN, результат не определен стандартом - это может быть один или другой в зависимости от любых внешних факторов, таких как выпуск или сборка отладки, версия компилятора, фаза луны и т. Д.

Другие вопросы по тегам