programa - Uso de funciones mínimas y máximas en C++
programa en c++ (14)
¿No podría una implementación de 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 de fminf , fmin y fminl , respectivamente?
Las especializaciones proporcionarían un mejor rendimiento para los tipos de punto flotante, mientras que la plantilla general manejaría los tipos de punto no flotante sin intentar forzar los tipos de punto flotante en tipos de punto flotante de la manera en que fmin s y fmax es.
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 (tal vez para mejorar la eficiencia, la portabilidad, la flexibilidad, etc.)?
Notas:
La biblioteca de plantillas estándar de C ++ (STL) declara las funciones
max
ymax
en el encabezado del algorithm estándar de C ++.El estándar C (C99) proporciona la función
fmax
yfmax
en el encabezado estándar de C math.h
¡Gracias por adelantado!
Como señaló Richard Corden, use las funciones C ++ mín. Y máx definidas en el espacio de nombres estándar. Proporcionan seguridad de tipos y ayudan a evitar la comparación de tipos mixtos (es decir, punto flotante frente a entero), lo que a veces puede ser indeseable.
Si encuentra que la biblioteca de C ++ que utiliza también define min / max como macros, puede causar conflictos, entonces puede evitar la sustitución de macros no deseada llamando a las funciones min / max de esta manera (observe corchetes adicionales):
(std::min)(x, y)
(std::max)(x, y)
Recuerde, esto efectivamente deshabilitará la en.wikipedia.org/wiki/Argument_dependent_name_lookup (ADL, también llamada búsqueda Koenig), en caso de que quiera confiar en ADL.
Como usted mismo observó, 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 de C99 se incorpora a C ++ (si es que existe), las áreas de aplicación de estas funciones están claramente separadas. No hay una situación en la que tengas que "preferir" una sobre la otra.
Solo usa templated std::min
/ std::max
en C ++, y use lo que esté disponible en C.
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 de 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
se deben 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 en la medida en que puedo decir que todas estas funciones están definidas por la implementación de todos modos, en este caso, para estar 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
puede ser emulado 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
.
Ver el ensamblaje muestra que Clang usa código incorporado para fmax
y fmax
mientras que GCC los llama desde una biblioteca matemática. El ensamblaje 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 usando -Ofast
fmax
convierte simplemente en
maxsd xmm0, xmm1
Así que esto demuestra una vez más que std::max
es un subconjunto de fmax
y que cuando se usa un modelo de punto flotante más suelto que no tiene nan
o cero con fmax
entonces fmax
y std::max
son iguales. El mismo argumento obviamente se aplica a fmin
y std::min
.
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
Preferiría las funciones mínimas / máximas de C ++, si está utilizando C ++, porque son específicas del tipo. fmin / fmax forzará que todo se convierta a / desde un punto flotante.
Además, las funciones mínimas / máximas de C ++ funcionarán con los tipos definidos por el usuario siempre que haya definido el operador <para esos tipos.
HTH
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 a dobles, lo que tendrá (al menos generalmente) un significado que es menor que 64 bits. Cuando convierte un número de este tipo a un doble, algunos de los bits menos significativos pueden perderse por completo.
Esto significa que dos números que realmente eran diferentes podrían terminar igual cuando se convierten al doble, y el resultado será ese número incorrecto, que no es necesariamente igual a ninguna de las entradas originales.
Siempre utilizo las macros min y max para ints. No estoy seguro de por qué alguien usaría fmin o fmax para valores enteros.
El big gotcha con min y max es que no son funciones, incluso si se parecen a ellas. Si haces algo como:
min (10, BigExpensiveFunctionCall())
Esa llamada de función puede ser llamada dos veces dependiendo de la implementación de la macro. Como tal, su mejor práctica en mi organización es nunca llamar mínimo o máximo con cosas que no sean literales o variables.
Te estás perdiendo todo el punto de fmin y fmax. Se incluyó en C99 para que las CPU modernas puedan usar sus instrucciones nativas (lea SSE) para los puntos flotantes mínimo y máximo y evitar una prueba y una derivación (y, por lo tanto, una derivación posiblemente errónea). Reescribí el código que usaba std :: min y std :: max para usar los intrínsecos SSE para min y max en bucles internos, y el aumento de velocidad fue significativo.
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 de rendimiento y portabilidad:
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}
fmin y fmax son solo para variables de punto flotante y doble.
min y max son funciones de plantilla que permiten la comparación de cualquier tipo, dado un predicado binario. También pueden usarse con otros algoritmos para proporcionar una funcionalidad compleja.
std :: min y std :: max son plantillas. Por lo tanto, se pueden usar en una variedad de tipos que brindan menos del operador, incluidos flotadores, dobles, dobles largos. Entonces, si quisieras escribir código genérico de C ++, harías 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 al rendimiento, no creo que fmax
y fmax
difieran de sus contrapartes de C ++.
fmax
y fmax
, de fminl
y fmaxl
pueden ser preferibles cuando se comparan enteros con signo y sin signo; puede aprovechar el hecho de que el rango completo de números con y sin signo no tiene que preocuparse por los rangos de enteros y las promociones.
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);
fmax
y fmax
son específicamente para uso con números de punto flotante (de ahí la "f"). Si lo usa para ints, puede sufrir pérdidas de rendimiento o precisión debido a la conversión, la sobrecarga de llamadas a funciones, etc., dependiendo de su compilador / plataforma.
std::min
y std::max
son funciones de plantilla (definidas en el encabezado <algorithm>
) 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 compilador no le permitirá convertir accidentalmente un int de 64 bits en un flotador de 64 bits, por ejemplo. Solo por esta razón, las plantillas deben ser su elección predeterminada. (Crédito a Matthieu M & bk1e)
Incluso cuando se usa con flotadores, la plantilla puede ganar en rendimiento. Un compilador siempre tiene la opción de incluir llamadas a funciones de plantilla, ya que el código fuente es parte de la unidad de compilación. A veces es imposible en línea una llamada a una función de biblioteca, por otro lado (bibliotecas compartidas, ausencia de optimización de tiempo de enlace, etc.).