Voici le programme de test:
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;
}
Dans Debug, j'obtiens:
slope is nan
maxThreshold is 1.79769e+308
the_min is nan
the_min is 1.79769e+308
En version, j'obtiens:
slope is nan
maxThreshold is 1.79769e+308
the_min is 1.79769e+308
the_min is nan
Pourquoi obtiendrais-je un résultat différent dans la version que dans le débogage?
J'ai déjà vérifié le post Stack Overflow tilisation des fonctions min et max en C++, et il ne mentionne aucune différence Release/Debug .
J'utilise Visual Studio 2015.
Je l'ai:
Voici l'implémentation utilisée par VS en mode débogage (avec _Pred
Étant DEBUG_LT
, LT pour 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);
}
Ce qui équivaut à (plus lisible):
if (!_Pred(_Left, _Right))
{
return false;
}
else
{
if ( _Pred(_Right, _Left) )
{
assert( false );
return true;
}
else
{
return true;
}
}
Ce qui, encore une fois, équivaut à (!_Pred(_Left, _Right))
. Transcrite sous forme de macro, elle devient #define _DEBUG_LT(x, y) !((y) < (x))
(c'est-à-dire: PAS droite <gauche).
L'implémentation des versions est en fait une macro #define _DEBUG_LT(x, y) ((x) < (y))
(c'est-à-dire: gauche <droite).
Les implémentations Debug (!(y<x))
Et Release (x<y)
Ne sont donc certainement pas les mêmes et se comportent différemment si un paramètre est un NaN ...! Ne demandez pas pourquoi ils ont fait ça ...
Dans IEEE 754 comparer NAN à n'importe quoi donnera toujours false
, peu importe ce que c'est.
slope > 0; // false
slope < 0; // false
slope == 0; // false
Et, plus important encore pour vous
slope < DBL_MAX; // false
DBL_MAX < slope; // false
Il semble donc que le compilateur réorganise les paramètres/utilise >
ou <=
au lieu de <
, et c'est pourquoi vous obtenez des résultats différents.
Par exemple, ces fonctions pourraient être décrites comme telles
Libération:
double const& min(double const& l, double const r) {
return l <= r ? l : r;
}
Déboguer:
double const& min(double const& l, double const& r) {
return r < l ? r : l;
}
Les exigences (LessThanComparable) sur std::min
à part, ceux-ci ont la même signification arithmétiquement. Mais ils donnent des résultats différents lorsque vous les utilisez avec NaN.
Vous n'avez pas spécifié le format de représentation en virgule flottante utilisé par votre processeur. Mais, puisque vous utilisez Visual Studio, je suppose que vous utilisez Windows, puis je suppose que votre processeur utilise la représentation IEEE 754 .
Dans IEEE 754, NaN n'est pas ordonné pour chaque numéro. Cela signifie que (NaN < f) == false
et (f < NaN) == false
pour toute valeur de f
. Pédantiquement, cela signifie que les nombres à virgule flottante qui prennent en charge NaN ne répondent pas aux exigences de LessThanComparable qui est une exigence pour std::min
. Pratiquement std::min
se comporte comme spécifié dans la norme tant qu'aucun des arguments n'est NaN.
Étant donné que l'un des arguments est NaN dans votre code, le résultat n'est pas spécifié par la norme - il pourrait être l'un ou l'autre en fonction de facteurs externes tels que la version vs la version de débogage, la version du compilateur, la phase de la lune, etc.