Почему 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
В выпуске я получаю:
slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan
Почему я должен получить другой результат в Release, чем Debug?
Я уже проверил Qaru post Использование функций min и max в С++, и он не упоминает различия Release/Debug.
Я использую Visual Studio 2015.
Ответы
Ответ 1
Получил это:
Вот реализация, используемая VS в режиме отладки (с _Pred
DEBUG_LT
, LT для Lower Than):
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))
(i.e: left < right).
Таким образом, реализация Debug (!(y<x))
и Release (x<y)
определенно не то же самое, и они ведут себя по-другому, если один параметр является NaN...! Не спрашивайте, почему они это сделали.
Ответ 2
В IEEE 754 сравнение NAN с чем угодно всегда будет давать false
, независимо от того, что это такое.
slope > 0; // false
slope < 0; // false
slope == 0; // false
И, что более важно для вас
slope < DBL_MAX; // false
DBL_MAX < slope; // false
Итак, кажется, что компилятор переупорядочивает параметры/использует >
или <=
вместо <
, и поэтому вы получаете разные результаты.
Например, эти функции могут быть описаны как таковые
Release:
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.
Ответ 3
Вы не указали, какой формат представления с плавающей запятой использует ваш процессор. Но, поскольку вы используете Visual Studio, я предполагаю, что вы используете Windows, а затем я буду считать, что ваш процессор использует IEEE 754.
В IEEE 754 NaN неупорядочен по отношению к каждому числу. Это означает, что (NaN < f) == false
и (f < NaN) == false
для любого значения f
. Педантично это означает, что числа с плавающей запятой, которые поддерживают NaN, не соответствуют требованиям LessThanComparable, что является требованием для std::min
. Практически std::min
ведет себя так, как указано в стандарте, если ни один из аргументов не равен NaN.
Поскольку один из аргументов является NaN в коде, результат не указан стандартом - он может быть одним или другим в зависимости от любых внешних факторов, таких как выпуск vs debug build, версия компилятора, фаза луны, и др.