Search code examples
c++nanminfloating-point-comparison

Why does Release/Debug have a different result for std::min?


Here is the test program:

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

In Debug, I get:

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

In Release, I get:

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

Why would I get a different result in Release than Debug?

I already checked Stack Overflow post Use of min and max functions in C++, and it does not mention any Release/Debug differences.

I am using Visual Studio 2015.


Solution

  • Got it:

    Here is the implementation used by VS in Debug mode (with _Pred being DEBUG_LT, LT for 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);
        }
    

    Which is equivalent to (more readable):

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

    Which, again is equivalent to (!_Pred(_Left, _Right)). Transcripted as a macro, it becomes #define _DEBUG_LT(x, y) !((y) < (x)) (i.e: NOT right < left).

    Release implementation is actually a macro #define _DEBUG_LT(x, y) ((x) < (y)) (i.e: left < right).

    So Debug (!(y<x)) and Release (x<y) implementations are definitely not the same and they do behave differently if one parameter is a NaN...! Don't ask why they did that....