fmin
和fmax
专门用于浮点数(因此有"f")。如果您对整数使用它,可能会因为转换、函数调用开销等原因而导致性能或精度损失,这取决于您的编译器/平台。
std::min
和std::max
是模板函数(定义在头文件<algorithm>
中),可以处理具有小于 (<
) 运算符的任何类型,因此可以处理允许这样比较的任何数据类型。如果不希望使用 <
进行比较,还可以提供自己的比较函数。
这样更安全,因为必须显式转换参数以匹配不同类型的参数。例如,编译器不会让您意外地将64位整数转换为64位浮点数。仅出于这个原因,模板应该成为您的默认选择。(感谢Matthieu M & bk1e)
即使在处理浮点数时,模板可能也会在性能上胜出。由于源代码是编译单元的一部分,编译器始终可以选择将调用模板函数的操作内联。在另一方面(共享库、缺乏链接时优化等)有时可能无法将调用库函数的操作内联。
std::min
,std::max
以及fmin
和fmax
之间有一个重要区别。
std::min(-0.0,0.0) = -0.0
std::max(-0.0,0.0) = -0.0
然而
fmin(-0.0, 0.0) = -0.0
fmax(-0.0, 0.0) = 0.0
所以std::min
并不完全等同于fmin
。函数std::min
和std::max
是不可交换的。要在双精度浮点数上使用fmin
和fmax
得到相同的结果,应该交换参数。
fmin(-0.0, 0.0) = std::min(-0.0, 0.0)
fmax(-0.0, 0.0) = std::max( 0.0, -0.0)
但据我所知,针对这种情况所有这些函数无论如何都是实现定义的,因此为了百分之百确定,您需要测试它们的实现方式。
x!= NaN
,还有另一个重要的区别。std::max(Nan,x) = NaN
std::max(x,NaN) = x
std::min(Nan,x) = NaN
std::min(x,NaN) = x
然而
fmax(Nan,x) = x
fmax(x,NaN) = x
fmin(Nan,x) = x
fmin(x,NaN) = x
fmax
可以通过以下代码模拟。
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);
}
这表明 std::max
是 fmax
的一个子集。
查看汇编代码可以发现,Clang 使用内置代码来处理 fmax
和 fmin
,而 GCC 则从数学库中调用它们。使用 -O3
选项时,Clang 对于 fmax
的汇编代码如下:
movapd xmm2, xmm0
cmpunordsd xmm2, xmm2
movapd xmm3, xmm2
andpd xmm3, xmm1
maxsd xmm1, xmm0
andnpd xmm2, xmm1
orpd xmm2, xmm3
movapd xmm0, xmm2
对于std::max(double, double)
,它只是
maxsd xmm0, xmm1
然而,对于使用-Ofast
的GCC和Clang,fmax
变得非常简单。
maxsd xmm0, xmm1
因此,这再次表明std::max
是fmax
的子集,并且当您使用较松的浮点模型时,该模型不具有nan
或有符号零,则fmax
和std::max
相同。显然,相同的论点适用于fmin
和std::min
。
_mm_max_sd
,它展示了maxsd既不会丢失nan也不会交换。http://coliru.stacked-crooked.com/a/768f6d831e79587f - Z boson你完全没有理解fmin和fmax的点。它们在C99中被引入,是为了让现代CPU可以使用它们的本地(即SSE)指令来进行浮点数最小值和最大值的运算,从而避免测试和分支(以及可能的错误预测分支)。我已经重新编写了使用std::min和std::max的代码,改用SSE内部循环中的min和max指令,速度显著提升。
-O3 -march=native
标志,这些差异可能会消失。 - David Stonestd::max<double>
甚至(a>b)?a:b
都映射到一个maxsd指令,而且在-O1优化级别下与-O0不同,对NaN的处理也不同... - greggostd::min和std::max是模板。因此,它们可以用于提供小于运算符的各种类型,包括浮点数、双精度浮点数、长双精度浮点数等。因此,如果你想编写通用的C++代码,你需要这样做:
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
}
关于性能,我认为fmin
和fmax
与它们的C++对应函数没有区别。
swap
和一些数值函数,如abs
。如果存在特殊的交换和绝对值函数,则应该使用类型的特殊函数而不是通用函数。我建议阅读Herb Sutter的关于“命名空间和接口原则”的文章:http://www.gotw.ca/publications/mill08.htm - sellibitze正如Richard Corden所指出的,使用std命名空间中定义的C++函数min和max。它们提供类型安全,并帮助避免比较混合类型(例如浮点数与整数),有时可能是不可取的。
如果您发现您使用的C++库也将min/max定义为宏,这可能会引起冲突,那么可以通过以下方式调用min/max函数来防止不必要的宏替换(请注意额外的括号):
(std::min)(x, y)
(std::max)(x, y)
请记住,这将有效地禁用参数依赖查找(ADL,也称为Koenig查找),以防您想要依赖ADL。
fmin
和fmax
是在C99中引入的。标准C++库没有fmin
和fmax
函数。直到C99标准库被纳入C++(如果有的话),这些函数的应用领域将得到清晰的区分。不存在必须“优先”使用其中一个函数的情况。std::min
/std::max
,并在C中使用可用的函数即可。使用 std::min
和 std::max
。
如果其他版本更快,那么您的实现可以添加这些重载,从而获得性能和可移植性的好处:
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}