Uso de funciones mínimas y máximas en C ++

Desde C ++, ¿son preferibles min y max sobre fmax y fmax ? Para comparar dos enteros, ¿proporcionan básicamente la misma funcionalidad?

¿Tiende a usar uno de estos conjuntos de funciones o prefiere escribir el suyo propio (quizás para mejorar la eficiencia, la portabilidad, la flexibilidad, etc.)?

Notas:

  1. La Biblioteca de plantillas estándar de C ++ (STL) declara las funciones min y max en el encabezado estándar del algoritmo de C ++.

  2. El estándar C (C99) proporciona la función fmax y fmax en el encabezado C math.h estándar.

¡Gracias por adelantado!

fmax y fmax son específicamente para usar con números de coma flotante (de ahí la “f”). Si lo usa para entradas, puede sufrir pérdidas de rendimiento o precisión debido a la conversión, a la sobrecarga de llamada de función, etc., dependiendo de su comstackdor / plataforma.

std::min y std::max son funciones de plantilla (definidas en el encabezado ) que funcionan en cualquier tipo con un operador menor que ( < ), por lo que pueden operar en cualquier tipo de datos que permita dicha comparación. También puede proporcionar su propia función de comparación si no desea que funcione < .

Esto es más seguro ya que tiene que convertir explícitamente los argumentos para que coincidan cuando tienen diferentes tipos. El comstackdor no le permitirá convertir accidentalmente un int de 64 bits en un flotante de 64 bits, por ejemplo. Esta sola razón debería hacer que las plantillas sean su elección predeterminada. (Crédito a Matthieu M & bk1e)

Incluso cuando se usa con flotadores, la plantilla puede ganar en rendimiento. Un comstackdor siempre tiene la opción de subrayar llamadas a funciones de plantilla ya que el código fuente es parte de la unidad de comstackción. A veces es imposible alinear una llamada a una función de biblioteca, por otro lado (bibliotecas compartidas, ausencia de optimización de tiempo de enlace, etc.).

Hay una diferencia importante entre std::min , std::max y fmax y fmax .

 std::min(-0.0,0.0) = -0.0 std::max(-0.0,0.0) = -0.0 

mientras

 fmin(-0.0, 0.0) = -0.0 fmax(-0.0, 0.0) = 0.0 

Entonces std::min no es un sustituto 1-1 para fmin . Las funciones std::min y std::max no son conmutativas. Para obtener el mismo resultado con dobles con fmax y fmax uno debe intercambiar los argumentos

 fmin(-0.0, 0.0) = std::min(-0.0, 0.0) fmax(-0.0, 0.0) = std::max( 0.0, -0.0) 

Pero, por lo que puedo decir, todas estas funciones se definen de todos modos en este caso, de modo que esté 100% seguro de que tiene que probar cómo se implementan.


Hay otra diferencia importante. Para x ! = NaN x ! = NaN :

 std::max(Nan,x) = NaN std::max(x,NaN) = x std::min(Nan,x) = NaN std::min(x,NaN) = x 

mientras

 fmax(Nan,x) = x fmax(x,NaN) = x fmin(Nan,x) = x fmin(x,NaN) = x 

fmax se puede emular con el siguiente código

 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); } 

Esto muestra que std::max es un subconjunto de fmax .

Al observar el conjunto, se muestra que Clang usa el código incorporado para fmax y fmax mientras que GCC los llama de una biblioteca matemática. El conjunto para clang para fmax con fmax es

 movapd xmm2, xmm0 cmpunordsd xmm2, xmm2 movapd xmm3, xmm2 andpd xmm3, xmm1 maxsd xmm1, xmm0 andnpd xmm2, xmm1 orpd xmm2, xmm3 movapd xmm0, xmm2 

mientras que para std::max(double, double) es simplemente

 maxsd xmm0, xmm1 

Sin embargo, para GCC y Clang, usar -Ofast fmax convierte simplemente

 maxsd xmm0, xmm1 

Entonces, esto muestra una vez más que std::max es un subconjunto de fmax y que cuando se usa un modelo de coma flotante más flexible que no tiene nan o cero firmado, entonces fmax y std::max son lo mismo. El mismo argumento obviamente se aplica a fmin y std::min .

Te estás perdiendo todo el punto de fmin y fmax. Se incluyó en C99 para que las CPU modernas pudieran usar sus instrucciones nativas (leer SSE) para coma flotante mínimo y máximo y evitar una prueba y bifurcación (y por lo tanto una posible ramificación mal predicha). He vuelto a escribir el código que usó std :: min y std :: max para usar intrínsecos SSE para min y max en bucles internos en su lugar y la aceleración fue significativa.

std :: min y std :: max son plantillas. Por lo tanto, se pueden usar en una variedad de tipos que proporcionan un operador menor que flotadores, dobles y dobles largos. Entonces, si quisieras escribir código C ++ genérico harías algo como esto:

 template 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 cuanto al rendimiento, no creo que fmax y fmax difieran de sus contrapartes de C ++.

Si su implementación proporciona un tipo de entero de 64 bits, puede obtener una respuesta diferente (incorrecta) utilizando fmin o fmax. Sus enteros de 64 bits se convertirán en dobles, que (al menos generalmente) tendrán un significado inferior a 64 bits. Cuando conviertes ese número en un doble, algunos de los bits menos significativos pueden / se perderán por completo.

Esto significa que dos números que eran realmente diferentes podrían terminar iguales cuando se convierten a doble, y el resultado será ese número incorrecto, que no es necesariamente igual a ninguna de las entradas originales.

Preferiría las funciones mín. / Máx. De C ++, si está usando C ++, porque son específicas del tipo. fmin / fmax forzará que todo se convierta a / desde punto flotante.

Además, las funciones mín. / Máx. De C ++ funcionarán con tipos definidos por el usuario siempre que haya definido el operador

HTH

Como usted mismo notó, fmax y fmax se introdujeron en C99. La biblioteca estándar de C ++ no tiene funciones fmax y fmax . Hasta que la biblioteca estándar C99 se incorpore a C ++ (si alguna vez), las áreas de aplicación de estas funciones se separan limpiamente. No hay ninguna situación en la que deba “preferir” una sobre la otra.

Simplemente usa templated std::min / std::max en C ++ y usa lo que está disponible en C.

Como señaló Richard Corden, use las funciones C ++ min y max definidas en el espacio de nombres estándar. Proporcionan seguridad de tipo y ayudan a evitar la comparación de tipos mixtos (es decir, punto flotante frente a número entero) lo que a veces puede ser indeseable.

Si encuentra que la biblioteca C ++ que utiliza define min / max como macros también, puede causar conflictos, entonces puede evitar la sustitución de macro no deseada llamando a las funciones mín / máx de esta manera (observe corchetes adicionales):

 (std::min)(x, y) (std::max)(x, y) 

Recuerde, esto efectivamente desactivará Argument Dependent Lookup (ADL, también llamada búsqueda de Koenig), en caso de que quiera confiar en ADL.

fmin y fmax son solo para variables de coma flotante y dobles.

min y max son funciones de plantilla que permiten la comparación de cualquier tipo, dado un predicado binario. También se pueden usar con otros algoritmos para proporcionar una funcionalidad compleja.

Use std::min y std::max .

Si las otras versiones son más rápidas, su implementación puede agregar sobrecargas para estas y obtendrá el beneficio del rendimiento y la portabilidad:

 template  T min (T, T) { // ... default } inline float min (float f1, float f2) { return fmin( f1, f2); } 

Por cierto, en cstdlib hay __min y __max que puedes usar.

Para más información: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

Siempre uso las macros min y max para ints. No estoy seguro de por qué alguien usaría fmin o fmax para valores enteros.

El gran problema con min y max es que no son funciones, incluso si se parecen a ellas. Si haces algo como:

 min (10, BigExpensiveFunctionCall()) 

Esa llamada a la función puede ser llamada dos veces dependiendo de la implementación de la macro. Como tal, es una buena práctica en mi organización el nunca llamar a minimo o máximo con cosas que no son literales o variables.

fmax y fmax , de fminl y fmaxl podrían ser preferidos al comparar enteros con y sin signo: puede aprovechar el hecho de que toda la gama de números firmados y no firmados no tiene que preocuparse por los rangos enteros y las promociones.

 unsigned int x = 4000000000; int y = -1; int z = min(x, y); z = (int)fmin(x, y); 

¿No podría una implementación en C ++ dirigida a procesadores con instrucciones SSE proporcionar especializaciones de std :: min y std :: max para los tipos float , double y long double que hacen el equivalente a fminf , fmin y fminl , respectivamente?

Las especializaciones proporcionarían un mejor rendimiento para los tipos de coma flotante, mientras que la plantilla general manejaría los tipos de punto flotante sin intentar forzar los tipos de coma flotante en tipos de punto flotante de esa manera lo harían los fmin y fmax .