从 C++ 开始,是std::min
和std::max
优先于fmin
和fmax
?对于比较两个整数,它们是否提供基本相同的功能?
您倾向于使用其中一组函数还是更喜欢编写自己的函数(也许是为了提高效率、可移植性、灵活性等)?
笔记:
提前致谢!
fmin
并且fmax
专门用于浮点数(因此是“f”)。如果您将它用于整数,您可能会因转换、函数调用开销等而遭受性能或精度损失,具体取决于您的编译器/平台。
std::min
并且std::max
是模板函数(在 header 中定义<algorithm>
),它适用于具有小于 ( <
) 运算符的任何类型,因此它们可以对允许此类比较的任何数据类型进行操作。如果您不希望它起作用,您也可以提供自己的比较功能<
。
这更安全,因为当它们具有不同类型时,您必须显式转换参数以匹配。例如,编译器不会让您意外地将 64 位 int 转换为 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
不是 1-1 的替代品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)
但据我所知,在这种情况下,所有这些函数都是实现定义的,所以要 100% 确定你必须测试它们是如何实现的。
还有一个重要的区别。对于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 从数学库中调用它们。clang for fmax
with的程序集-O3
是
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
但是,对于 GCC 和 Clang,使用-Ofast
fmax
变得简单
maxsd xmm0, xmm1
因此,这再次表明,当您使用没有或没有零符号的较宽松的浮点模型时,它std::max
是 和 的子集,并且是相同的。同样的论点显然适用于和。fmax
nan
fmax
std::max
fmin
std::min
您错过了 fmin 和 fmax 的全部内容。它包含在 C99 中,因此现代 CPU 可以使用其本机(读取 SSE)指令来处理浮点最小值和最大值,并避免测试和分支(因此可能是错误预测的分支)。我已经重写了使用 std::min 和 std::max 的代码,以在内部循环中使用 SSE 内在函数来代替 min 和 max,并且加速非常重要。
std::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++ 同行不同。
如果您的实现提供 64 位整数类型,您可能会通过使用 fmin 或 fmax 得到不同的(不正确的)答案。您的 64 位整数将被转换为双精度数,这将(至少通常)具有小于 64 位的有效数字。当您将这样的数字转换为双精度数时,一些最低有效位可能/将完全丢失。
这意味着两个真正不同的数字在转换为双精度时最终可能相等——结果将是那个不正确的数字,不一定等于原始输入中的任何一个。
如果您使用 C++,我更喜欢 C++ min/max 函数,因为它们是特定于类型的。fmin/fmax 将强制将所有内容转换为浮点数/从浮点数转换。
此外,只要您为这些类型定义了 operator<,C++ min/max 函数就可以使用用户定义的类型。
高温高压
正如您自己指出的那样,fmin
并fmax
在 C99 中进行了介绍。标准 C++ 库没有fmin
和fmax
功能。在 C99 标准库被合并到 C++ 之前(如果有的话),这些函数的应用领域是完全分离的。没有任何情况下您可能不得不“偏爱”其中一个。
您只需在 C++ 中使用模板化std::min
/ ,并使用 C 中可用的任何内容。std::max
正如 Richard Corden 所指出的,使用在 std 命名空间中定义的 C++ 函数 min 和 max 。它们提供类型安全,并有助于避免比较混合类型(即浮点与整数),这有时可能是不受欢迎的。
如果您发现您使用的 C++ 库也将 min/max 定义为宏,这可能会导致冲突,那么您可以防止不需要的宏替换以这种方式调用 min/max 函数(注意额外的括号):
(std::min)(x, y)
(std::max)(x, y)
请记住,如果您想依赖 ADL ,这将有效地禁用Argument Dependent Lookup (ADL,也称为 Koenig 查找)。
fmin 和 fmax 仅适用于浮点和双精度变量。
min 和 max 是模板函数,允许在给定二元谓词的情况下比较任何类型。它们还可以与其他算法一起使用以提供复杂的功能。
使用std::min
和std::max
。
如果其他版本更快,那么您的实现可以为这些版本添加重载,您将获得性能和可移植性的好处:
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}
顺便说一句,cstdlib
里面有__min
,__max
你可以使用。
针对具有 SSE 指令的处理器的 C++ 实现不能为float、double和long double类型提供std::min和std::max的特化,它们分别相当于fminf、fmin和fminl吗?
特化将为浮点类型提供更好的性能,而通用模板将处理非浮点类型,而不会像fmin和fmax那样尝试将浮点类型强制转换为浮点类型。
我总是将 min 和 max 宏用于整数。我不确定为什么有人会使用 fmin 或 fmax 作为整数值。
min 和 max 最大的问题是它们不是函数,即使它们看起来像它们。如果您执行以下操作:
min (10, BigExpensiveFunctionCall())
根据宏的实现,该函数调用可能会被调用两次。因此,我的组织中的最佳实践是永远不要用不是文字或变量的东西调用 min 或 max。
fmin
和fmax
, offminl
和fmaxl
在比较有符号和无符号整数时可能是首选 - 您可以利用有符号和无符号数字的整个范围这一事实,而不必担心整数范围和促销。
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);