在C++中使用min和max函数

bpo*_*ter 69 c c++ max min

从C++,是minmax优选超过fminfmax?为了比较两个整数,它们是否提供基本相同的功能?

您是倾向于使用这些功能集中的一个还是更喜欢自己编写(可能是为了提高效率,可移植性,灵活性等)?

笔记:

  1. C++标准模板库(STL)在标准C++ 算法头中声明minmax函数.

  2. C标准(C99)提供标准C math.h标头中的函数fminfmax函数.

提前致谢!

Cog*_*eel 107

fmin并且fmax特别适用于浮点数(因此为"f").如果将其用于整数,则可能会因转换,函数调用开销等而导致性能或精度损失,具体取决于您的编译器/平台.

std::min并且std::max是模板函数(在标题中定义<algorithm>),它适用于任何具有less-than(<)运算符的类型,因此它们可以对允许这种比较的任何数据类型进行操作.如果您不想让它起作用,您也可以提供自己的比较功能<.

这更安全,因为当它们具有不同类型时必须显式转换参数以匹配.例如,编译器不会让您意外地将64位int转换为64位浮点数.仅此原因应该使模板成为您的默认选择.(感谢Matthieu M&bk1e)

即使与浮动一起使用,模板也可能在性能上获胜.编译器始终可以选择内联对模板函数的调用,因为源代码是编译单元的一部分.有时,不可能内联对库函数的调用(共享库,缺少链接时优化等).

  • 哪个是好东西™IMO :) (47认同)
  • 警告:min和max只能比较完全相同类型的两个变量...所以你不能用它们比较int和double :( (8认同)
  • 真 - 最大(1,2.0)不起作用,它必须是max <double>(1,2.0)或max(double(1),2.0). (5认同)
  • 这是一个很大的假设,即转换成本.在某些系统上,唯一的区别是在比较之前将值加载到FPU Regester而不是正常寄存器. (2认同)

Z b*_*son 32

有一个重要区别std::min,std::maxfminfmax.

std::min(-0.0,0.0) = -0.0
std::max(-0.0,0.0) = -0.0
Run Code Online (Sandbox Code Playgroud)

fmin(-0.0, 0.0) = -0.0
fmax(-0.0, 0.0) =  0.0
Run Code Online (Sandbox Code Playgroud)

所以std::min不是1-1的替代品fmin.功能std::minstd::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)
Run Code Online (Sandbox Code Playgroud)

但据我所知,所有这些功能都是在这种情况下定义的实现,所以要100%确定你必须测试它们是如何实现的.


还有另一个重要的区别.用于x ! = NaN:

std::max(Nan,x) = NaN
std::max(x,NaN) = x
std::min(Nan,x) = NaN
std::min(x,NaN) = x
Run Code Online (Sandbox Code Playgroud)

fmax(Nan,x) = x
fmax(x,NaN) = x
fmin(Nan,x) = x
fmin(x,NaN) = x
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

这表明它std::max是一个子集fmax.

查看程序集显示Clang使用内置代码fmax,fmin而GCC从数学库中调用它们.用于铛的组件,fmax-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
Run Code Online (Sandbox Code Playgroud)

std::max(double, double)它简直就是这样

maxsd   xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

然而,对于GCC和Clang使用-Ofast fmax变得简单

maxsd   xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

因此,这再次表明std::max的一个子集fmax,并且当您使用不具有较宽松的浮点模型nan或再签署零fmaxstd::max是相同的.同样的论点显然适用于fminstd::min.

  • @greggo,C 标准[声明](http://port70.net/~nsz/c/c11/n1570.html#F.10.9.2)“理想情况下,fmax 对零的符号敏感,例如fmax(-0.0, +0.0) 将返回 +0;但是,在软件中实现可能不切实际。”。因此,这不是 fmin/fmax 的要求,而是偏好。当我测试这些函数时,它们会执行首选操作。 (2认同)

小智 16

你错过了fmin和fmax的整个点.它包含在C99中,因此现代CPU可以使用其本机(读取SSE)指令来实现浮点最小值和最大值,并避免测试和分支(因此可能是错误预测的分支).我重写了使用std :: min和std :: max的代码,在内部循环中使用SSE内在函数来获取min和max,而且加速很快.

  • 也许他在测试时没有打开优化,否则编译器试图编译一个可以"随处"运行的二进制文件,因此不知道它可以使用SSE.我怀疑使用gcc,如果你传递了标志`-O3 -march = native`,差异就会消失 (5认同)
  • 它被包含在C中的真正原因是因为C没有模板或函数重载,因此它们创建了一个不同于浮点类型的最大命名函数. (3认同)

sel*_*tze 6

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
}
Run Code Online (Sandbox Code Playgroud)

至于性能,我不认为fminfmax他们的C++对应物不同.


Jer*_*fin 6

如果您的实现提供64位整数类型,则可以使用fmin或fmax获得不同(不正确)的答案.您的64位整数将转换为双精度数,它将(至少通常)具有小于64位的有效位数.当您将这样的数字转换为double时,一些最低有效位可能/将完全丢失.

这意味着两个真正不同的数字在转换为double时最终会相等 - 结果将是不正确的数字,这不一定等于任何一个原始输入.