Utilisation des fonctions min et max en C++
à Partir de c/" class="blnk">C++, sont min
et max
prime sur le fmin
et fmax
? Pour comparer deux entiers, fournissent-ils essentiellement la même fonctionnalité?
avez-vous tendance à utiliser un de ces ensembles de fonctions ou préférez-vous écrire les vôtres (peut-être pour améliorer l'efficacité, la portabilité, la flexibilité, etc.)?
Notes:
-
la bibliothèque de modèles Standard C++ (STL) déclare les fonctions
min
etmax
dans l'en-tête standard C++ algorithme . -
la norme C (C99) fournit la fonction
fmin
etfmax
dans la norme C math.h en-tête.
Merci d'avance!
14 réponses
fmin
et fmax
sont spécifiquement pour une utilisation avec des nombres à virgule flottante (d'où le "f"). Si vous l'utilisez pour ints, vous pouvez subir des pertes de performance ou de précision dues à la conversion, à la fonction Appel overhead, etc. selon votre compilateur / plate-forme.
std::min
et std::max
sont des templates de fonctions (définies dans l'en-tête <algorithm>
) qui fonctionnent sur n'importe quel type avec un moins-que ( <
) de l'opérateur, de sorte qu'ils peuvent fonctionner sur n'importe quel type de données qui permet une telle comparaison. Vous pouvez également fournir votre propre fonction de comparaison si vous ne voulez pas que cela fonctionne <
.
c'est plus sûr puisque vous devez convertir explicitement les arguments pour correspondre quand ils ont des types différents. Le compilateur ne vous laissera pas accidentellement convertir un int 64-bit en un flotteur 64-bit, par exemple. Cette seule raison devrait faire des modèles votre choix par défaut. (Crédit à Matthieu M & bk1e)
même lorsqu'il est utilisé avec des flotteurs le modèle mai gagner en performance. Un compilateur a toujours la possibilité de lancer des appels vers des fonctions de template puisque le code source fait partie de l'Unité de compilation. Parfois il est impossible d'inline un appel à une fonction de bibliothèque, d'autre part (bibliothèques partagées, absence d'optimisation du temps de lien, etc.).
il y a une différence importante entre std::min
, std::max
et fmin
et fmax
.
std::min(-0.0,0.0) = -0.0
std::max(-0.0,0.0) = -0.0
attendu que
fmin(-0.0, 0.0) = -0.0
fmax(-0.0, 0.0) = 0.0
de sorte que std::min
ne remplace pas 1-1 pour fmin
. Les fonctions std::min
et std::max
ne sont pas commutatives. Pour obtenir le même résultat avec les doubles avec fmin
et fmax
il faut échanger les arguments
fmin(-0.0, 0.0) = std::min(-0.0, 0.0)
fmax(-0.0, 0.0) = std::max( 0.0, -0.0)
mais pour autant que je puisse dire toutes ces fonctions sont implémentation défini de toute façon dans ce cas donc pour être sûr à 100% que vous devez tester comment ils sont mis en œuvre.
il y a une autre différence importante. Pour x ! = NaN
:
std::max(Nan,x) = NaN
std::max(x,NaN) = x
std::min(Nan,x) = NaN
std::min(x,NaN) = x
attendu que
fmax(Nan,x) = x
fmax(x,NaN) = x
fmin(Nan,x) = x
fmin(x,NaN) = x
fmax
peut être émulé avec le code suivant
double myfmax(double x, double y)
{
// z > nan for z != nan is required by C the standard
int xnan = isnan(x), ynan = isnan(y);
if(xnan || ynan) {
if(xnan && !ynan) return y;
if(!xnan && ynan) return x;
return x;
}
// +0 > -0 is preferred by C the standard
if(x==0 && y==0) {
int xs = signbit(x), ys = signbit(y);
if(xs && !ys) return y;
if(!xs && ys) return x;
return x;
}
return std::max(x,y);
}
Cela montre que std::max
est un sous-ensemble de fmax
.
en regardant l'Assemblée montre que Clang utilise le code de builtin pour fmax
et fmin
tandis que GCC les appelle d'une bibliothèque de mathématiques. L'assemblage pour clang pour fmax
avec -O3
est
movapd xmm2, xmm0
cmpunordsd xmm2, xmm2
movapd xmm3, xmm2
andpd xmm3, xmm1
maxsd xmm1, xmm0
andnpd xmm2, xmm1
orpd xmm2, xmm3
movapd xmm0, xmm2
alors que pour std::max(double, double)
c'est simplement
maxsd xmm0, xmm1
Cependant, pour GCC et Clang en utilisant -Ofast
fmax
devient simplement
maxsd xmm0, xmm1
donc cela montre une fois de plus que std::max
est un sous-ensemble de fmax
et que lorsque vous utilisez un modèle à virgule flottante plus lâche qui n'a pas nan
ou zéro signé alors fmax
et std::max
sont les mêmes. Le même argument s'applique évidemment à fmin
et std::min
.
vous manquez le point entier de fmin et fmax. Il a été inclus dans C99 afin que les CPU modernes puissent utiliser leurs instructions natives (SSE lu) pour la pointe flottante min et max et éviter un test et une branche (et donc une branche peut-être mal prédite). J'ai réécrit le code qui utilisait std::min et std::max pour utiliser SSE intrinsics pour min et max dans les boucles internes à la place et l'accélération était significative.
std::min et std::max sont des modèles. Ainsi, ils peuvent être utilisés sur une variété de types qui fournissent le moins que l'opérateur, y compris les flotteurs, doubles, doubles longs. Donc, si vous vouliez écrire du code C++ Générique, vous feriez quelque chose comme ceci:
template<typename T>
T const& max3(T const& a, T const& b, T const& c)
{
using std::max;
return max(max(a,b),c); // non-qualified max allows ADL
}
quant à la performance, Je ne pense pas que fmin
et fmax
diffèrent de leurs homologues C++.
si votre implémentation fournit un type entier 64 bits, vous pouvez obtenir une réponse différente (incorrecte) en utilisant fmin ou fmax. Vos entiers 64 bits seront convertis en doubles, ce qui aura (au moins habituellement) une signification plus petite que 64 bits. Lorsque vous convertissez un tel nombre en un double, certains des bits les moins significatifs peuvent/seront complètement perdus.
cela signifie que deux nombres qui étaient vraiment différents pouvaient finir égaux quand convertis en double -- et le résultat sera que nombre incorrect, ce n'est pas nécessairement égal à l'une des entrées initiales.
je préférerais les fonctions C++ min/max, si vous utilisez C++, parce qu'elles sont spécifiques au type. fmin/fmax force tout être converties ou de virgule flottante.
de plus, les fonctions min/max de C++ fonctionneront avec les types définis par l'utilisateur aussi longtemps que vous avez défini l'opérateur< pour ces types.
HTH
comme vous l'avez vous-même noté, fmin
et fmax
ont été introduits en C99. La bibliothèque C++ Standard n'a pas les fonctions fmin
et fmax
. Jusqu'à ce que la bibliothèque standard C99 soit incorporée dans C++ (si jamais), les zones d'application de ces fonctions sont clairement séparées. Il n'y a pas de situation où vous avez à "préférer" l'un sur l'autre.
vous venez d'utiliser templated std::min
/ std::max
en C++, et utilisez tout ce qui est disponible en C.
comme L'a souligné Richard Corden, utilisez les fonctions C++ min et max définies dans l'espace de noms std. Ils offrent une sécurité de type, et aident à éviter de comparer les types mixtes (i.e. float point vs integer) ce qui peut parfois être indésirable.
si vous trouvez que la bibliothèque C++ que vous utilisez définit min / max comme macros ainsi, elle peut causer des conflits, alors vous pouvez empêcher la macro substitution indésirable appelant les fonctions min/max de cette façon (notez les crochets supplémentaires):
(std::min)(x, y)
(std::max)(x, y)
rappelez-vous, cela va effectivement désactiver Argument dependent Lookup (ADL, également appelé Koenig lookup), dans le cas où vous voulez compter sur ADL.
fmin et fmax sont seulement pour les nombres à virgule flottante et les variables doubles.
min et max sont des fonctions de modèle qui permettent la comparaison de tous les types, étant donné un prédicat binaire. Ils peuvent également être utilisés avec d'autres algorithmes de fournir des fonctionnalités complexes.
utiliser std::min
et std::max
.
si les autres versions sont plus rapides, alors votre implémentation peut ajouter des surcharges pour celles-ci et vous bénéficierez des performances et de la portabilité:
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}
soit dit en passant, dans cstdlib
il y a __min
et __max
que vous pouvez utiliser.
pour plus de détails: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx
j'utilise toujours les macros min et max pour les ints. Je ne suis pas sûr pourquoi quelqu'un utiliserait fmin ou fmax pour des valeurs entières.
le grand gotcha avec min et max est que ce ne sont pas des fonctions, même si elles leur ressemblent. Si vous faites quelque chose comme:
min (10, BigExpensiveFunctionCall())
cet appel de fonction peut être appelé deux fois en fonction de l'implémentation de la macro. En tant que tel, sa meilleure pratique dans mon organisation de ne jamais appeler min ou max avec des choses qui ne sont pas un littéral ou variable.
fmin
et fmax
, de fminl
et fmaxl
pourrait être préféré en comparant signés et non signés entiers - vous pouvez profiter du fait que l'ensemble de la gamme de signés et non signés nombres et vous n'avez pas à vous soucier des gammes entières et des promotions.
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);
ne pouvait pas une implémentation C++ ciblée pour les processeurs avec des instructions SSE fournir des spécialisations de std::min et std::max pour les types float , double , et long double qui font l'équivalent de fminf , Fmin , et fminl , respectivement?
le les spécialisations fourniraient de meilleures performances pour les types à virgule flottante alors que le modèle général traiterait les types à virgule non flottante sans tenter de contraindre les types à virgule flottante en types à virgule flottante comme le feraient les fmin s et fmax es.