En C++, min
et max
sont-ils préférables à fmin
et fmax
? Pour comparer deux nombres entiers, fournissent-ils essentiellement les mêmes fonctionnalités?
Avez-vous tendance à utiliser l'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
et max
dans l'en-tête standard C++ algorithm .
Le standard C (C99) fournit la fonction fmin
et fmax
dans l'en-tête standard C math.h .
Merci d'avance!
fmin
et fmax
sont spécifiquement conçus pour être utilisés avec des nombres à virgule flottante (d'où le "f"). Si vous l'utilisez pour ints, vous risquez de subir des pertes de performances ou de précision dues à la conversion, aux frais généraux des appels de fonction, etc., en fonction de votre compilateur/plate-forme.
std::min
et std::max
sont des fonctions modèles (définies dans l’en-tête <algorithm>
) qui fonctionne sur tous les types dont le nombre est inférieur à (<
), ils peuvent donc utiliser n'importe quel type de données permettant une telle comparaison. Vous pouvez également fournir votre propre fonction de comparaison si vous ne souhaitez pas que cela fonctionne <
.
Ceci est plus sûr car vous devez convertir explicitement les arguments en correspondance quand ils ont des types différents. Le compilateur ne vous laissera pas convertir accidentellement un int de 64 bits en un float de 64 bits, par exemple. Cette seule raison devrait faire des modèles votre choix par défaut. (Merci à Matthieu M & bk1e)
Même lorsqu'il est utilisé avec des flotteurs, le modèle peut gagne en performance. Un compilateur a toujours la possibilité d'inclure des appels à des fonctions de modèle car le code source fait partie de l'unité de compilation. Parfois, il est en revanche impossible d'inclure un appel à une fonction de bibliothèque (bibliothèques partagées, absence d'optimisation du temps de liaison, etc.).
Il existe 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
tandis que
fmin(-0.0, 0.0) = -0.0
fmax(-0.0, 0.0) = 0.0
Donc std::min
N'est pas un substitut 1-1 de fmin
. Les fonctions std::min
Et std::max
Ne sont pas commutatives. Pour obtenir le même résultat avec des 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 sache, toutes ces fonctions sont définies de toute façon dans ce cas donc pour être sûr à 100%, vous devez tester comment elles sont mises 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
tandis 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, Clang utilise le code intégré pour fmax
et fmin
alors que GCC les appelle à partir d'une bibliothèque mathématique. 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, utiliser -Ofast
fmax
devient simplement
maxsd xmm0, xmm1
Cela montre donc 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
ni signé zéro, alors fmax
et std::max
sont identiques. 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 processeurs modernes puissent utiliser leurs instructions natives (lire SSE) pour les valeurs à virgule flottante min et max et éviter un test et une branche (et donc une branche éventuellement 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) et l'accélération était significative.
std :: min et std :: max sont des modèles. Donc, ils peuvent être utilisés sur une variété de types qui fournissent le moins que l'opérateur, y compris les flotteurs, les doubles, les doubles longs. Donc, si vous voulez é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
}
En ce qui concerne les performances, 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) à l'aide de fmin ou de fmax. Vos entiers 64 bits seront convertis en doubles, qui auront (du moins généralement) une signification inférieure à 64 bits. Lorsque vous convertissez un tel nombre en un double, certains des bits les moins significatifs peuvent être/seront complètement perdus.
Cela signifie que deux nombres qui étaient vraiment différents pourraient devenir égaux une fois convertis en doublons - et le résultat sera ce nombre incorrect, qui ne sera pas nécessairement égal à l'une des entrées d'origine.
Je préférerais les fonctions C++ min/max, si vous utilisez C++, car elles sont spécifiques au type. fmin/fmax forcera tout à être converti en/en virgule flottante.
De plus, les fonctions C++ min/max fonctionneront avec les types définis par l'utilisateur tant que vous aurez défini l'opérateur <pour ces types.
HTH
Comme vous l'avez noté vous-même, fmin
et fmax
ont été introduits dans 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 à C++ (si jamais), les domaines d'application de ces fonctions sont clairement séparés. Il n'y a pas de situation où vous pourriez avoir à "préférer" l'un par rapport à l'autre.
Vous venez d'utiliser des templates 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 fournissent une sécurité de type et évitent de comparer des types mélangés (c'est-à-dire un nombre à virgule flottante à un nombre entier), ce qui peut parfois être indésirable.
Si vous trouvez que la bibliothèque C++ que vous utilisez définit également les macros min/max, cela peut entraîner des conflits, vous pouvez alors empêcher toute substitution de macro non souhaitée en appelant les fonctions min/max de cette façon (remarquez les crochets supplémentaires):
(std::min)(x, y)
(std::max)(x, y)
N'oubliez pas que cela désactivera effectivement recherche dépendante de l'argument (ADL, également appelée recherche Koenig), au cas où vous souhaiteriez vous fier à ADL.
Utilisation std::min
et std::max
.
Si les autres versions sont plus rapides, 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);
}
Une implémentation C++ destinée aux processeurs avec SSE) ne peut-elle pas 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?
Les spécialisations fourniraient de meilleures performances pour les types à virgule flottante, tandis que le modèle général gérera les types à virgule non flottante sans tenter de contraindre les types à virgule flottante à des types à virgule flottante de la manière suivante: fmin s et - fmax es.
À propos, dans cstdlib
, il y a __min
et __max
vous pouvez utiliser.
Pour plus d'informations: http://msdn.Microsoft.com/zh-cn/library/btkhtd8d.aspx
fmin et fmax ne concernent que les variables à virgule flottante et les variables doubles.
min et max sont des fonctions de modèle qui permettent la comparaison de tous types, à partir d’un prédicat binaire. Ils peuvent également être utilisés avec d'autres algorithmes pour fournir des fonctionnalités complexes.
fmin
et fmax
, de fminl
et fmaxl
pourraient être préférés lors de la comparaison d'entiers signés et non signés - vous pouvez profiter du fait que toute la plage de numéros signés et non signés et vous n'avez pas à vous soucier des plages entières et des promotions.
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);
J'utilise toujours les macros min et max pour ints. Je ne sais pas pourquoi quelqu'un utiliserait fmin ou fmax pour les valeurs entières.
Le gros problème avec min et max, c'est qu'ils ne sont pas des fonctions, même s'ils 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, il est recommandé dans mon organisation de ne jamais appeler min ou max avec des éléments qui ne sont ni littéraux ni variables.