为什么发布/调试对std::min有不同的结果?

时间:2022-05-23 22:51:38

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.

我已经检查过在c++中使用最小和最大函数的栈溢出,它没有提到任何版本/调试差异。

I am using Visual Studio 2015.

我正在使用Visual Studio 2015。

3 个解决方案

#1


35  

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

在IEEE 754中,将NAN与任何事物相比较总是会产生错误,无论它是什么。

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.

对std::min的要求(没有可比性),那些在算术上有相同的意思。但是当你和NaN一起使用时,它们会产生不同的结果。

#2


26  

Got it:

明白了:

Here is the implementation used by VS in Debug mode (with _Pred being DEBUG_LT, LT for Lower Than):

下面是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);
    }

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

也就是(!_Pred(_Left _Right))。通过宏脚本,它变成了#define _DEBUG_LT(x, y) !(y) < (x)) (i)。e:不是右 <左)。< p>

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

发布实现实际上是一个宏#define _DEBUG_LT(x, y) (x) (x) < (y))) (i)。艾凡:左 <右)。< p>

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

所以Debug (!(y ))和release>

#3


22  

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.

您没有指定处理器使用的浮点表示格式。但是,由于您使用的是Visual Studio,所以我假设您使用的是Windows,然后我假设您的处理器使用的是IEEE 754表示。

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.

在IEEE 754中,NaN对每个数字都是无序的。这意味着,(NaN < f) == false, (f < NaN) == false,这意味着支持NaN的浮点数不满足std的要求:min。实际上std::min的行为与标准中指定的一样,只要两个参数都不是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.

由于其中一个参数是您的代码中的NaN,因此结果根据标准是不确定的——它可以是一个,也可以是另一个,这取决于任何外部因素,比如版本vs调试构建、版本编译器、阶段月亮等等。

#1


35  

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

在IEEE 754中,将NAN与任何事物相比较总是会产生错误,无论它是什么。

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.

对std::min的要求(没有可比性),那些在算术上有相同的意思。但是当你和NaN一起使用时,它们会产生不同的结果。

#2


26  

Got it:

明白了:

Here is the implementation used by VS in Debug mode (with _Pred being DEBUG_LT, LT for Lower Than):

下面是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);
    }

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

也就是(!_Pred(_Left _Right))。通过宏脚本,它变成了#define _DEBUG_LT(x, y) !(y) < (x)) (i)。e:不是右 <左)。< p>

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

发布实现实际上是一个宏#define _DEBUG_LT(x, y) (x) (x) < (y))) (i)。艾凡:左 <右)。< p>

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

所以Debug (!(y ))和release>

#3


22  

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.

您没有指定处理器使用的浮点表示格式。但是,由于您使用的是Visual Studio,所以我假设您使用的是Windows,然后我假设您的处理器使用的是IEEE 754表示。

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.

在IEEE 754中,NaN对每个数字都是无序的。这意味着,(NaN < f) == false, (f < NaN) == false,这意味着支持NaN的浮点数不满足std的要求:min。实际上std::min的行为与标准中指定的一样,只要两个参数都不是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.

由于其中一个参数是您的代码中的NaN,因此结果根据标准是不确定的——它可以是一个,也可以是另一个,这取决于任何外部因素,比如版本vs调试构建、版本编译器、阶段月亮等等。