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

前端 未结 3 1305
一整个雨季
一整个雨季 2020-12-16 09:10

Here is the test program:

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

    std::cout          


        
相关标签:
3条回答
  • 2020-12-16 09:42

    In IEEE 754 comparing NAN to anything will always yield false, no matter what it is.

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

    And, more importantly for you

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

    So it seems that the compiler reorders the parameters/uses > or <= instead of <, and that's why you get the differing results.

    For example, those functions could be described as such

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

    The requirements (LessThanComparable) on std::min aside, those have the same meaning arithmetically. But they yield different results when you use them with NaN.

    0 讨论(0)
  • 2020-12-16 09:48

    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....

    0 讨论(0)
  • 2020-12-16 09:57

    You didn't specify which floating point representation format your processor uses. But, since you use Visual Studio, I'll assume that you use Windows, and then I'll assume that your processor uses IEEE 754 representation.

    In IEEE 754, NaN is unordered in respect to every number. This means that (NaN < f) == false and (f < NaN) == false for any value of f. Pedantically, this means that floating point numbers that support NaN do not meet the requirements of LessThanComparable which is a requirement for std::min. Practically std::min behaves as specified in the standard as long as neither argument is NaN.

    Since one of the arguments is NaN in your code, the result is unspecified by the standard - it could be one or the other depending on any external factors such as release vs debug build, version of compiler, phase of the moon, etc.

    0 讨论(0)
提交回复
热议问题