2009-10-27 16 views
57

De C++, ¿son min y max preferibles sobre fmin y fmax? Para comparar dos enteros, ¿proporcionan básicamente la misma funcionalidad?Uso de funciones mín. Y máx. En C++

¿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 C (STL) declara el min y max funciones en el estándar de C++ algorithm cabecera.

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

¡Gracias de antemano!

Respuesta

86

fmin 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 compilador/plataforma.

std::min y std::max son funciones de plantilla (definidas en la cabecera de <algorithm>) que funcionan en cualquier tipo con un menor que (<) operador, por lo que puede funcionar en cualquier tipo de datos que permite una comparación de este tipo. 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 compilador 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 utiliza con flotadores de la plantilla puede victoria en el rendimiento. Un compilador siempre tiene la opción de subrayar llamadas a funciones de plantilla ya que el código fuente es parte de la unidad de compilación. A veces es imposible para alinear una llamada a una función de biblioteca, por otro lado (bibliotecas compartidas, ausencia de optimización de tiempo de enlace, etc.).

+7

Advertencia: min y max solo pueden comparar dos variables del mismo tipo ... por lo que no puede comparar un int y un doble con ellos :( –

+3

True - max (1, 2.0) no funciona, tiene ser algo así como max (1, 2.0) o max (double (1), 2.0). –

+39

Que es una buena cosa IMO :) – Cogwheel

0

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

La gran sorpresa con min y max es que no son funciones, incluso si se parecen a ellas. Si hace algo como:

min (10, BigExpensiveFunctionCall()) 

Esa llamada a la función puede recibir dos llamadas 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.

+8

std :: min no es una macro ... – Cogwheel

+6

min y max a menudo se implementan como macros en C, pero esto es C++, donde se implementan como plantillas. Mucho, mucho mejor. –

+3

Si '#include ', obtiene 'min' y' max' definidos como macros. Esto entrará en conflicto con 'std :: min' y' std :: max', por lo que debe compilar sus fuentes con '#define NOMINMAX' para excluir el primero. –

4

Preferiría las funciones mínimas/máximas de C++, si está utilizando C++, porque son específicas de cada 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 < para esos tipos.

HTH

6

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. Por lo tanto, si desea escribir código C++ genérica que haría algo como esto:

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 cuanto a rendimiento, no creo fmin y fmax difieren de sus homólogos de C++.

+1

¿Qué es ADL y por qué lo queremos aquí? –

+0

@Rob Kennedy: http: //en.wikipedia.org/wiki/Argument_dependent_name_lookup – bk1e

+1

ADL = búsqueda dependiente del argumento. En este caso, probablemente no sea necesario porque cada tipo definido por el usuario que viene con su propia función máxima probablemente también proporcione un operador especial menor que. Es solo un hábito mío que escribo código como este, principalmente con 'swap' y algunas funciones numéricas como' abs'. * Desea * utilizar las funciones especiales de intercambio y abs de un tipo en lugar de las genéricas en caso de que existan las especiales. Sugiero leer el artículo de Herb Sutter sobre "espacios de nombres y el principio de interfaz": http://www.gotw.ca/publications/mill08.htm – sellibitze

1

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.

2

Como notó usted mismo, fmin y fmax se introdujeron en C99. La biblioteca estándar de C++ no tiene las funciones fmin 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.

que acaba de utilizar con plantilla std::min/std::max en C++, y el uso de lo que está disponible en C.

6

Si su aplicación proporciona un tipo entero de 64 bits, es posible obtener una respuesta diferente (incorrecta) mediante el uso de 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 en dobles, y el resultado será ese número incorrecto, que no es necesariamente igual a ninguna de las entradas originales.

0

fmin y fmax, de fminl y fmaxl podría ser preferido cuando se comparan firmado y enteros sin signo - se puede aprovechar el hecho de que toda la gama de números con y sin signo y usted no tiene que preocuparse por rangos de números enteros y promociones.

unsigned int x = 4000000000; 
int y = -1; 

int z = min(x, y); 
z = (int)fmin(x, y); 
+0

¿por qué no hay especializaciones que manejen estos casos? –

1

Uso std::min y std::max.

Si las otras versiones son más rápidos a continuación, su aplicación puede añadir sobrecargas de éstos y obtendrá el beneficio de rendimiento y portabilidad:

template <typename T> 
T min (T, T) { 
    // ... default 
} 

inline float min (float f1, float f2) { 
return fmin(f1, f2); 
}  
2

Como señaló Richard Corden, utilice las funciones de C++ mínimo y máximo se define en espacio de nombres estándar Proporcionan seguridad de tipo y ayudan a evitar la comparación de tipos mixtos (es decir, punto flotante versus número entero) lo que a veces puede ser indeseable.

Si encuentra que la biblioteca C++ utiliza define min/max como macros, así, puede causar conflictos, entonces se puede prevenir no deseados sustitución de macro llamando al min/funciones max esta manera (Aviso soportes adicionales):

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

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

13

Falta el punto completo 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.

+1

¿Qué tan grande fue la aceleración? ¿Por qué no puede el compilador de C++ detectar cuando está usando std :: min ? –

+4

Quizás no tuvo activada la optimización cuando probó, o si no, el compilador intentaba compilar un archivo binario que podía ejecutarse "en cualquier lugar" y por lo tanto no sabía que podía usar SSE. Sospecho que al usar gcc, las diferencias desaparecerían si pasas los flags '-O3 -march = native' –

+2

La verdadera razón por la que se incluyó en C fue porque C no tiene plantillas o función de sobrecarga, por lo que crean un función nombrada que solo máxima para tipos de coma flotante. –

14

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

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

mientras que

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

Así std::min no es un sustituto para el 1-1 fmin. Las funciones std::min y std::max no son conmutativas. Para obtener el mismo resultado con dobles junto fmin y fmax uno debe cambiar 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 yo puedo decir all these functions are implementation defined anyway in this case Así que para estar 100% seguro de lo que tiene que probar cómo se implementan.


Hay otra diferencia importante. Para x ! = NaN:

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

mientras que

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

fmax puede ser emulado por el código siguiente

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 mirar el conjunto, se muestra que Clang usa el código incorporado para fmax y fmin, mientras que GCC los llama desde una biblioteca matemática.El montaje de sonido metálico de fmax con -O3 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 Sonido metálico utilizando -Ofastfmax se convierte simplemente en

maxsd xmm0, xmm1 

Así lo muestra esta Una vez más, std::max es un subconjunto de fmax y que cuando utiliza un modelo de punto 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.

0

No se pudo aplicación de C++ apuntado para procesadores con instrucciones SSE proporcionar especializaciones de std :: min y std :: máximo para este tipo de flotador , doble, y doble largo, que haga lo equivalente a fminf, fmin y fminl, respectivamente?

Las especializaciones proporcionarían un mejor rendimiento para este tipo de punto flotante, mientras que el modelo general se ocuparía de los tipos no de punto flotante sin tratar de coaccionar a los tipos de punto flotante en tipos de coma flotante de esa manera la fmin s y fmax es would.

+0

Intel C++ tiene un mejor rendimiento para std :: min que fmin. En gcc, un buen rendimiento de fmin requiere una configuración de matemática finita que lo rompe para los operandos no finitos. – tim18

Cuestiones relacionadas