为什么 Release/Debug 对 std::min 有不同的结果?
Why does Release/Debug have a different result for 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;
}
在调试中,我得到:
slope is nan
maxThreshold is 1.79769e+308
the_min is nan
the_min is 1.79769e+308
在 Release 中,我得到:
slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan
为什么我在 Release 中得到的结果与 Debug 中的结果不同?
我已经检查了 Stack Overflow post Use of min and max functions in C++,它没有提到任何 Release/Debug 差异。
我正在使用 Visual Studio 2015.
在 IEEE 754 中,将 NAN 与任何东西进行比较总是会产生 false
,无论它是什么。
slope > 0; // false
slope < 0; // false
slope == 0; // false
而且,对您来说更重要的是
slope < DBL_MAX; // false
DBL_MAX < slope; // false
所以编译器似乎重新排序 parameters/uses >
或 <=
而不是 <
,这就是你得到不同结果的原因。
例如,这些函数可以这样描述
发布:
double const& min(double const& l, double const r) {
return l <= r ? l : r;
}
调试:
double const& min(double const& l, double const& r) {
return r < l ? r : l;
}
除了 std::min
的要求 (LessThanComparable),它们在算术上具有相同的含义。但是当您将它们与 NaN 一起使用时,它们会产生不同的结果。
您没有指定您的处理器使用哪种浮点表示格式。但是,由于您使用 Visual Studio,我假设您使用 Windows,然后我假设您的处理器使用 IEEE 754 表示。
在 IEEE 754 中,NaN 对于每个数字都是无序的。这意味着 (NaN < f) == false
和 (f < NaN) == false
对于 f
的任何值。迂腐地说,这意味着支持 NaN 的浮点数不满足 LessThanComparable 的要求,而 LessThanComparable 是 std::min
的要求。实际上 std::min
只要两个参数都不是 NaN,就会按照标准中指定的方式运行。
由于您的代码中的参数之一是 NaN,因此标准未指定结果 - 它可能是其中之一,具体取决于任何外部因素,例如发布与调试构建、编译器版本、阶段月亮等
知道了:
这里是VS在Debug模式下使用的实现(_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))
(即:NOT right < left)。
发布实现实际上是一个宏#define _DEBUG_LT(x, y) ((x) < (y))
(即:left < right)
所以 Debug (!(y<x))
和 Release (x<y)
实现绝对不一样,如果一个参数是 NaN,它们的行为确实不同......!不要问他们为什么那样做....
这里是测试程序:
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;
}
在调试中,我得到:
slope is nan
maxThreshold is 1.79769e+308
the_min is nan
the_min is 1.79769e+308
在 Release 中,我得到:
slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan
为什么我在 Release 中得到的结果与 Debug 中的结果不同?
我已经检查了 Stack Overflow post Use of min and max functions in C++,它没有提到任何 Release/Debug 差异。
我正在使用 Visual Studio 2015.
在 IEEE 754 中,将 NAN 与任何东西进行比较总是会产生 false
,无论它是什么。
slope > 0; // false
slope < 0; // false
slope == 0; // false
而且,对您来说更重要的是
slope < DBL_MAX; // false
DBL_MAX < slope; // false
所以编译器似乎重新排序 parameters/uses >
或 <=
而不是 <
,这就是你得到不同结果的原因。
例如,这些函数可以这样描述
发布:
double const& min(double const& l, double const r) {
return l <= r ? l : r;
}
调试:
double const& min(double const& l, double const& r) {
return r < l ? r : l;
}
除了 std::min
的要求 (LessThanComparable),它们在算术上具有相同的含义。但是当您将它们与 NaN 一起使用时,它们会产生不同的结果。
您没有指定您的处理器使用哪种浮点表示格式。但是,由于您使用 Visual Studio,我假设您使用 Windows,然后我假设您的处理器使用 IEEE 754 表示。
在 IEEE 754 中,NaN 对于每个数字都是无序的。这意味着 (NaN < f) == false
和 (f < NaN) == false
对于 f
的任何值。迂腐地说,这意味着支持 NaN 的浮点数不满足 LessThanComparable 的要求,而 LessThanComparable 是 std::min
的要求。实际上 std::min
只要两个参数都不是 NaN,就会按照标准中指定的方式运行。
由于您的代码中的参数之一是 NaN,因此标准未指定结果 - 它可能是其中之一,具体取决于任何外部因素,例如发布与调试构建、编译器版本、阶段月亮等
知道了:
这里是VS在Debug模式下使用的实现(_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))
(即:NOT right < left)。
发布实现实际上是一个宏#define _DEBUG_LT(x, y) ((x) < (y))
(即:left < right)
所以 Debug (!(y<x))
和 Release (x<y)
实现绝对不一样,如果一个参数是 NaN,它们的行为确实不同......!不要问他们为什么那样做....