为什么Release/Debug对std :: min有不同的结果?

jpo*_*o38 41 c++ nan min floating-point-comparison

这是测试程序:

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;
}
Run Code Online (Sandbox Code Playgroud)

在Debug中,我得到:

slope is nan
maxThreshold is 1.79769e+308
the_min is nan
the_min is 1.79769e+308
Run Code Online (Sandbox Code Playgroud)

在发布中,我得到:

slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan
Run Code Online (Sandbox Code Playgroud)

为什么我会在Release中获得与调试不同的结果?

我已经检查了Stack Overflow post C++中使用min和max函数,并没有提到任何Release/Debug差异.

我正在使用Visual Studio 2015.

krz*_*zaq 37

IEEE 754中,将NAN与任何东西进行比较false,无论它是什么,总会产生.

slope > 0; // false
slope < 0; // false
slope == 0; // false
Run Code Online (Sandbox Code Playgroud)

而且,更重要的是对你而言

slope < DBL_MAX; // false
DBL_MAX < slope; // false
Run Code Online (Sandbox Code Playgroud)

因此,似乎编译器重新排序参数/使用><=代替<,这就是为什么你得到不同的结果.

例如,这些功能可以这样描述

发布:

double const& min(double const& l, double const r) {
    return l <= r ? l : r;
}
Run Code Online (Sandbox Code Playgroud)

调试:

double const& min(double const& l, double const& r) {
    return r < l ? r : l;
}
Run Code Online (Sandbox Code Playgroud)

除了要求(LessThanComparable)std::min之外,那些具有相同意义的算术.但是当它们与NaN一起使用时,它们会产生不同的结果.

  • 对于任何可能感兴趣的人的另一点:在IEEE754中将NAN与任何事物进行比较_包括其自身_将总是产生"假".顺便说一句,[这个问题](http://stackoverflow.com/questions/10034149/why-is-nan-not-equal-to-nan)是一个很好的答案,并有一套有趣的答案 - 包括几个相当有趣的在评论中的混战. (3认同)

jpo*_*o38 26

得到它了:

这是在调试模式下使用VS(与实施_PredDEBUG_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);
    }
Run Code Online (Sandbox Code Playgroud)

这相当于(更具可读性):

    if (!_Pred(_Left, _Right))
    {
        return false;
    }
    else
    {
        if ( _Pred(_Right, _Left) )
        {
            assert( false );
            return true;
        }
        else
        {
            return true;
        }
    }
Run Code Online (Sandbox Code Playgroud)

其中,再次相当于(!_Pred(_Left, _Right)).转录为宏,它变为#define _DEBUG_LT(x, y) !((y) < (x))(即:不是右<左).

发布实现实际上是一个宏#define _DEBUG_LT(x, y) ((x) < (y))(即:左<右).

所以调试(!(y<x))和发布(x<y)实现肯定不一样,如果一个参数是NaN,它们的行为会有所不同......!不要问他们为什么那样做......

  • 好吧,他们这样做是为了捕捉错误.在调试版本中,它以两种方式计算表达式,并断言它们产生相同的答案.在发布版本中,您假设应用程序已经过调试并且比较器是正确的,因此您不希望支付两次比较的代价. (6认同)
  • 当然,但至少,他们可以确保两者最终得到相同的结果......通过以不同的顺序检查两种方式本来是可能的. (2认同)

eer*_*ika 22

您没有指定处理器使用哪种浮点表示格式.但是,既然你使用Visual Studio,我会假设你使用Windows,然后我假设你的处理器使用IEEE 754表示.

在IEEE 754中,NaN对于每个数字都是无序的.这意味着(NaN < f) == false(f < NaN) == false任何价值f.小心地说,这意味着支持NaN的浮点数不符合LessThanComparable的要求std::min.std::min只要两个参数都不是NaN,实际上就会按照标准中的规定行事.

由于其中一个参数是代码中的NaN,因此标准未指定结果 - 它可能是一个或另一个,具体取决于任何外部因素,如发布与调试版本,编译器版本,月相等.

  • 等等,实际上,它看起来像*cppreference页面*是错误的,并且多年来一直缺少严格的弱排序要求之一:将`equiv(a,b)`定义为`!(a <b)&& !(b <a)`,要求`equiv(a,b)&& equiv(b,c)`暗示`equiv(a,c)`.NaN违反了该要求,因此看起来浮点数确实不满足LessThanComparable. (2认同)