【问题标题】:Use of min and max functions in C++在 C++ 中使用 min 和 max 函数
【发布时间】:2010-12-10 13:39:07
【问题描述】:

在 C++ 中,std::minstd::max 是否优于 fminfmax?对于比较两个整数,它们提供的功能是否基本相同?

您倾向于使用其中一组函数还是更喜欢编写自己的函数(也许是为了提高效率、可移植性、灵活性等)?

注意事项:

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

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

提前致谢!

【问题讨论】:

    标签: c++ c max min


    【解决方案1】:

    我总是将 min 和 max 宏用于整数。我不确定为什么有人会使用 fmin 或 fmax 作为整数值。

    min 和 max 的最大问题是它们不是函数,即使它们看起来像它们。如果您执行以下操作:

    min (10, BigExpensiveFunctionCall())
    

    根据宏的实现,该函数调用可能会被调用两次。因此,我的组织中的最佳实践是永远不要用不是文字或变量的东西调用 min 或 max。

    【讨论】:

    • min 和 max 通常在 C 中实现为宏,但在 C++ 中,它们被实现为模板。好多了。
    • 如果你 #include <windows.h>,你会得到 minmax 定义为宏。这将与std::minstd::max 冲突,因此您需要使用#define NOMINMAX 编译源以排除前者。
    • 如果微软在他们的<algorithm> 标头中加入一个#ifdef _WINDOWS #undef min 那就太好了。节省我的精力
    • @MSalters:好主意,但这不是标准库的责任。他们不应该用这样的通用名称污染命名空间。
    • std::min 有一个奇怪的陷阱:它实际上接受两个 const 引用,并返回其中一个。通常,它会被编译器折叠。但是我曾经有一个std::min( x, constval),其中constval在课堂上被定义为static const int constval=10;。而且,我收到了一个链接错误:undefined MyClass::constval。因为,现在 constval 必须存在,因为正在引用它。可以使用std::min( x, constval+0) 修复
    【解决方案2】:

    fminfmax 专门用于浮点数(因此是“f”)。如果将其用于整数,则可能会因转换、函数调用开销等而遭受性能或精度损失,具体取决于您的编译器/平台。

    std::minstd::max 是模板函数(在标题 <algorithm> 中定义),它们适用于具有小于 (<) 运算符的任何类型,因此它们可以对任何允许这样的数据类型进行操作比较。如果您不希望它在< 上工作,您也可以提供自己的比较功能。

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

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

    【讨论】:

    • 警告:min 和 max 只能比较完全相同类型的两个变量...所以你不能用它们比较 int 和 double :(
    • True - max(1, 2.0) 不起作用,它必须类似于 max(1, 2.0) 或 max(double(1), 2.0)。
    • 这是一件好事™ IMO :)
    • 这是一个很大的假设,即转换会产生成本。在某些系统上,唯一的区别是将值加载到 FPU 寄存器而不是比较之前的普通寄存器。
    • 是否有任何平台具有 64 位整数 (ILP64) 和 64 位双精度数?在这些平台上,从 int 转换为 double 会导致极端正/负 int 的精度损失。
    【解决方案3】:

    如果您使用 C++,我更喜欢 C++ 最小/最大函数,因为它们是特定于类型的。 fmin/fmax 将强制将所有内容转换为浮点数。

    此外,只要您为这些类型定义了 operator

    HTH

    【讨论】:

      【解决方案4】:

      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
      }
      

      至于性能,我认为fminfmax 与它们的C++ 对应物没有什么不同。

      【讨论】:

      • 什么是 ADL,我们为什么要在这里使用它?
      • ADL = 参数相关查找。在这种情况下,可能没有必要,因为每个带有自己的 max 函数的用户定义类型也可能提供特殊的小于运算符。我写这样的代码只是我的一个习惯——主要是用swap 和一些像abs 这样的数字函数。您会想要使用类型的特殊 swap 和 abs 函数而不是通用函数,以防特殊函数存在。建议阅读 Herb Sutter 关于“命名空间和接口原理”的文章:gotw.ca/publications/mill08.htm
      【解决方案5】:

      fmin 和 fmax 仅适用于浮点和双精度变量。

      min 和 max 是模板函数,允许在给定二元谓词的情况下比较任何类型。它们还可以与其他算法一起使用以提供复杂的功能。

      【讨论】:

        【解决方案6】:

        正如您自己所说,fminfmax 是在 C99 中引入的。标准 C++ 库没有 fminfmax 函数。在 C99 标准库被合并到 C++ 之前(如果有的话),这些函数的应用领域是完全分离的。没有任何情况下您可能不得不“偏爱”其中一个。

        您只需在 C++ 中使用模板化的 std::min/std::max,并使用 C 中可用的任何内容。

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            fminfmaxfminlfmaxl 在比较有符号和无符号整数时可能是首选 - 您可以利用有符号和无符号数字的整个范围而您没有不必担心整数范围和促销。

            unsigned int x = 4000000000;
            int y = -1;
            
            int z = min(x, y);
            z = (int)fmin(x, y);
            

            【讨论】:

            • 为什么没有专门处理这些案件的专业?
            【解决方案9】:

            使用std::minstd::max

            如果其他版本更快,那么您的实现可以为这些版本添加重载,您将获得性能和可移植性的好处:

            template <typename T>
            T min (T, T) {
              // ... default
            }
            
            inline float min (float f1, float f2) {
             return fmin( f1, f2);
            }    
            

            【讨论】:

              【解决方案10】:

              正如 Richard Corden 所指出的,使用在 std 命名空间中定义的 C++ 函数 min 和 max。它们提供类型安全,并有助于避免比较混合类型(即浮点与整数),这有时可能是不可取的。

              如果您发现您使用的 C++ 库也将 min/max 定义为宏,这可能会导致冲突,那么您可以防止不必要的宏替换以这种方式调用 min/max 函数(注意额外的括号):

              (std::min)(x, y)
              (std::max)(x, y)
              

              请记住,如果您想依赖 ADL,这将有效地禁用 Argument Dependant Lookup(ADL,也称为 Koenig 查找)。

              【讨论】:

                【解决方案11】:

                您错过了 fmin 和 fmax 的全部内容。它包含在 C99 中,因此现代 CPU 可以使用其本地(读取 SSE)指令来处理浮点最小值和最大值,并避免测试和分支(因此可能是错误预测的分支)。我已经重写了使用 std::min 和 std::max 的代码,以在内部循环中使用 SSE 内在函数来代替 min 和 max,并且速度提升非常显着。

                【讨论】:

                • 加速有多大?为什么 C++ 编译器无法检测到您何时使用 std::min
                • 也许他在测试时没有打开优化,或者编译器试图编译一个可以在“任何地方”运行的二进制文件,因此不知道它可以使用 SSE。我怀疑使用 gcc,如果您通过标志 -O3 -march=native,差异会消失
                • 它被包含在 C 中的真正原因是因为 C 没有模板或函数重载,因此他们创建了一个名称不同的函数,而不仅仅是浮点类型的 max。
                • 刚刚在 g++4.8 上尝试过:fmax、std::max&lt;double&gt; 甚至 (a&gt;b)?a:b 都映射到 -O1 上的单个 maxsd 指令。 (因此您对 NaN 的处理方式与 -O0 不同...)
                【解决方案12】:

                顺便说一句,cstdlib中有__min__max可以使用。

                更多:http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

                【讨论】:

                  【解决方案13】:

                  std::minstd::maxfminfmax 之间有一个重要区别。

                  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::minstd::max 不可交换。要获得与 fminfmax 的双打相同的结果,应该交换参数

                  fmin(-0.0, 0.0) = std::min(-0.0,  0.0)
                  fmax(-0.0, 0.0) = std::max( 0.0, -0.0)
                  

                  但据我所知all these functions are implementation defined anyway in this case,所以要 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::maxfmax 的子集。

                  查看程序集显示 Clang 使用 fmaxfmin 的内置代码,而 GCC 从数学库中调用它们。 fmax-O3 的 clang 程序集是

                  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::maxfmax 的子集,并且当您使用没有nan 或符号零的更宽松的浮点模型时,fmaxstd::max 是相同的.同样的论点显然适用于fminstd::min

                  【讨论】:

                  • maxsd/minsd 指令在丢弃 Nan 方面符合 fmax、fmin。但是,给定两个不同符号的零,它们不会选择最大或最小符号。但是,我找不到任何说明 fmax,fmin 是 defined 以这种方式处理零的文档。 +0 和 -0 通常被认为是等效的,除非定义了特定的行为。我相信没有理由不将 MAXSD 用于 fmax,不管 -Ofast。另外,我认为 std::max 可能会或可能不会映射到 fmax,具体取决于您包含的标头(从而改变了它对待 Nan 的方式)。
                  • @greggo,C 标准 states " 理想情况下,fmax 对零的符号很敏感,例如 fmax(-0.0, +0.0) 将返回 +0;但是,在软件中实现可能不切实际。”所以这不是 fmin/fmax 的要求,而是偏好。当我测试这些功能时,它们会做首选的事情。
                  • @greggo,我在回答中这么说。查看代码中的 cmets “// z > nan for z != nan is required by C the standard”和“// +0 > -0 is Preferred by C the standard”。
                  • @greggo,我测试了你关于 maxsd/minsd drop nan 的说法,这不是我观察到的 coliru.stacked-crooked.com/a/ca78268b6b9f5c88。操作员不会像带符号的零一样通勤。
                  • @greggo,这是一个更好的例子,我使用了_mm_max_sd,它表明 maxsd 既不掉线也不掉线。 coliru.stacked-crooked.com/a/768f6d831e79587f
                  【解决方案14】:

                  针对具有 SSE 指令的处理器的 C++ 实现不能为 float 类型提供 std::minstd::max 的特化>、doublelong double 相当于 fminffminfminl,分别?

                  特化将为浮点类型提供更好的性能,而通用模板将处理非浮点类型,而不会尝试以 fmin 的方式将浮点类型强制转换为浮点类型s 和 fmaxes 会。

                  【讨论】:

                  • 英特尔 c++ 的 std::min 性能优于 fmin。在 gcc 中,fmin 的良好性能需要仅限有限数学设置,这会破坏非有限操作数。
                  猜你喜欢
                  • 1970-01-01
                  • 2021-12-01
                  • 2017-08-06
                  • 2013-02-10
                  • 1970-01-01
                  • 2019-09-21
                  • 2013-04-24
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多