web-dev-qa-db-fra.com

Utilisation des fonctions min et max en C ++

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:

  1. La bibliothèque de modèles standard C++ (STL) déclare les fonctions min et max dans l'en-tête standard C++ algorithm .

  2. Le standard C (C99) fournit la fonction fmin et fmax dans l'en-tête standard C math.h .

Merci d'avance!

64
bporter

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

100
Cogwheel

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

30
Z boson

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.

16
J Ridges

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

6
sellibitze

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.

6
Jerry Coffin

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

4
Eric Melski

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.

2
AnT

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.

2
mloskot

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);
}    
1
Richard Corden

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.

1
user3405743

À 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

1
Justme0

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.

1
Marcin

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);
0
Eclipse

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.

0
popester