Saltar al contenido

Uso de funciones min y max en C++

Al fin luego de mucho batallar hemos dado con la respuesta de esta dificultad que agunos usuarios de este espacio han tenido. Si deseas aportar algo más no dudes en aportar tu información.

Solución:

fmin y fmax son específicamente para usar con números de coma 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., según su compilador/plataforma.

std::min y std::max son funciones de plantilla (definidas en el encabezado ) que funcionan en cualquier tipo con menos de (<) operador, para que puedan operar en cualquier tipo de datos que permita tal 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 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 mayo 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 para insertar 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 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

Entonces std::min no es un sustituto 1-1 para fmin. Las funciones std::min y std::max no son conmutativos. Para obtener el mismo resultado con dobles con fmin 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 están definidas por implementación de todos modos en este caso, por lo que para estar 100% seguro, debe 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 se puede emular con el siguiente código

double myfmax(double x, double y)

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

Observar el ensamblado muestra que Clang usa código integrado para fmax y fmin mientras que GCC los llama desde una biblioteca matemática. La asamblea para clang para 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 sencillo

maxsd   xmm0, xmm1

Sin embargo, para GCC y Clang usando -Ofastfmax se vuelve simplemente

maxsd   xmm0, xmm1

Así que esto demuestra una vez más que std::max es un subconjunto de fmax y que cuando usa un modelo de punto flotante más flexible que no tiene nan o firmado cero entonces fmax y std::max son lo mismo. Evidentemente, el mismo argumento 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 el punto flotante mínimo y máximo y evitar una prueba y una bifurcación (y, por lo tanto, una bifurcación posiblemente mal predicha). Reescribí el código que usaba std::min y std::max para usar SSE intrínsecos para min y max en bucles internos y la aceleración fue significativa.

Tienes la posibilidad compartir este ensayo si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *