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 -Ofast
fmax
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.