87

From C++, are std::min and std::max preferable over fmin and fmax? For comparing two integers, do they provide basically the same functionality?

Do you tend to use one of these sets of functions or do you prefer to write your own (perhaps to improve efficiency, portability, flexibility, etc.)?

Notes:

  1. The C++ Standard Template Library (STL) declares the min and max functions in the standard C++ algorithm header.

  2. The C standard (C99) provides the fmin and fmax function in the standard C math.h header.

starball
  • 20,030
  • 7
  • 43
  • 238
bporter
  • 3,572
  • 4
  • 24
  • 23

14 Answers14

122

fmin and fmax are specifically for use with floating point numbers (hence the "f"). If you use it for ints, you may suffer performance or precision losses due to conversion, function call overhead, etc. depending on your compiler/platform.

std::min and std::max are template functions (defined in header <algorithm>) which work on any type with a less-than (<) operator, so they can operate on any data type that allows such a comparison. You can also provide your own comparison function if you don't want it to work off <.

This is safer since you have to explicitly convert arguments to match when they have different types. The compiler won't let you accidentally convert a 64-bit int into a 64-bit float, for example. This reason alone should make the templates your default choice. (Credit to Matthieu M & bk1e)

Even when used with floats the template may win in performance. A compiler always has the option of inlining calls to template functions since the source code is part of the compilation unit. Sometimes it's impossible to inline a call to a library function, on the other hand (shared libraries, absence of link-time optimization, etc.).

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
Cogwheel
  • 22,781
  • 4
  • 49
  • 67
  • 9
    Warning: min and max can only compare two variables of the exact same type... so you can't compare an int and a double with them :( – Matthieu M. Oct 27 '09 at 16:53
  • 6
    True - max(1, 2.0) doesn't work, it has to be something like max(1, 2.0) or max(double(1), 2.0). – David Thornley Oct 27 '09 at 16:54
  • 55
    Which is a Good Thing™ IMO :) – Cogwheel Oct 27 '09 at 16:56
  • 2
    That's a big assumption that there will be a cost for conversion. On some systems the only difference will be loading the values into an FPU Regester rather than a normal register before comparison. – Martin York Oct 27 '09 at 17:00
  • Well yeah, pretty much every statement about C++ performance depends on the compiler/system. :P I'll change "'ll" to " may"... – Cogwheel Oct 27 '09 at 17:09
  • 1
    Are there any platforms with 64-bit ints (ILP64) and 64-bit doubles? On those platforms, converting from int to double would result in a loss of precision for extremely positive/negative ints. – bk1e Oct 28 '09 at 01:59
  • While it's true it may not be possible to inline library functions, it probably is often possible in the case of simple standard functions like fmax. [It's a gcc builtin](http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html). – poolie Sep 13 '13 at 23:26
  • @bk1e Tho loss off precision would be in both directions. – Tomáš Zato Jan 11 '16 at 11:39
53

There is an important difference between std::min, std::max and fmin and fmax.

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

whereas

fmin(-0.0, 0.0) = -0.0
fmax(-0.0, 0.0) =  0.0

So std::min is not a 1-1 substitute for fmin. The functions std::min and std::max are not commutative. To get the same result with doubles with fmin and fmax one should swap the arguments

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

But as far as I can tell all these functions are implementation defined anyway in this case so to be 100% sure you have to test how they are implemented.


There is another important difference. For x ! = NaN:

std::max(Nan,x) = NaN
std::max(x,NaN) = x
std::min(Nan,x) = NaN
std::min(x,NaN) = x

whereas

fmax(Nan,x) = x
fmax(x,NaN) = x
fmin(Nan,x) = x
fmin(x,NaN) = x

fmax can be emulated with the following code

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);
}

This shows that std::max is a subset of fmax.

Looking at the assembly shows that Clang uses builtin code for fmax and fmin whereas GCC calls them from a math library. The assembly for clang for fmax with -O3 is

movapd  xmm2, xmm0
cmpunordsd      xmm2, xmm2
movapd  xmm3, xmm2
andpd   xmm3, xmm1
maxsd   xmm1, xmm0
andnpd  xmm2, xmm1
orpd    xmm2, xmm3
movapd  xmm0, xmm2

whereas for std::max(double, double) it is simply

maxsd   xmm0, xmm1

However, for GCC and Clang using -Ofast fmax becomes simply

maxsd   xmm0, xmm1

So this shows once again that std::max is a subset of fmax and that when you use a looser floating point model which does not have nan or signed zero then fmax and std::max are the same. The same argument obviously applies to fmin and std::min.

Z boson
  • 32,619
  • 11
  • 123
  • 226
  • The maxsd/minsd instructions comply with fmax, fmin in terms of dropping Nan. But, given two zeros of different signs, they do not select the max or min sign. However, I can't find any documentation that says fmax,fmin are *defined* to handle zeros in this way. +0 and -0 are generally considered equivalent except where specific behaviour is defined. I believe there's no reason not to use MAXSD for fmax, regardless of -Ofast. Also, I think std::max may or may not be mapped to fmax, depending on what headers you've included (thus changing how it treats Nan). – greggo Feb 03 '19 at 17:23
  • 2
    @greggo, the C standard [states](http://port70.net/~nsz/c/c11/n1570.html#F.10.9.2) " Ideally, fmax would be sensitive to the sign of zero, for example fmax(-0.0, +0.0) would return +0; however, implementation in software might be impractical. ". So it's not a requirement for fmin/fmax but a preference. When I tested these functions they do the preferred thing. – Z boson Feb 05 '19 at 07:57
  • @greggo, I said this in my answer. Look at the comments in the code "// z > nan for z != nan is required by C the standard" and "// +0 > -0 is preferred by C the standard ". – Z boson Feb 05 '19 at 07:58
  • @greggo, I tested your claim that maxsd/minsd drop nan and that's not what I observe http://coliru.stacked-crooked.com/a/ca78268b6b9f5c88. The operators don't commute just like with signed zero. – Z boson Feb 05 '19 at 08:13
  • @greggo, here is a better example where I used `_mm_max_sd` which shows that maxsd neither drops nan nor commutes. http://coliru.stacked-crooked.com/a/768f6d831e79587f – Z boson Feb 05 '19 at 08:33
  • I tried both of those on gcc-7.4, get the same results as you: -O2 -ffast-math prints nans, and -O2 by itself doesn't. However, on examining the generated code, I see no execution of the min or max instructions, the compiler is folding the call at compile time (in the second one there is a function with maxsd, but it's not called). – greggo Dec 07 '19 at 20:18
  • You're right - If I break the second one into two C files, so the compiler is forced to call `myfmax`, the result is `-nan 1.000000` regardless of -ffastmath. So, it also seems gcc is not accurately modelling maxsd instruction in compile-time folding, which presumably is considered ok under -ffast-math ... – greggo Dec 07 '19 at 20:35
  • OK, it's not quite what I said for the second one. Without -ffast-math, it inlines the fmax into main, and actually does the 0.0/0.0 with a divsd and it gives `-nan 1.000000`. With -ffast-math it just works it all out at compile time and gives `nan nan` – greggo Dec 07 '19 at 20:42
18

You're missing the entire point of fmin and fmax. It was included in C99 so that modern CPUs could use their native (read SSE) instructions for floating point min and max and avoid a test and branch (and thus a possibly mis-predicted branch). I've re-written code that used std::min and std::max to use SSE intrinsics for min and max in inner loops instead and the speed-up was significant.

J Ridges
  • 181
  • 1
  • 2
  • 1
    How big was the speedup? Why can't the C++ compiler detect when you're using std::min? – Janus Troelsen Dec 07 '11 at 17:00
  • 5
    Perhaps he did not have optimization turned on when he tested, or else the compiler was trying to compile a binary that could run 'anywhere' and thus didn't know it could use SSE. I suspect that using gcc, the differences would disappear if you passed the flags `-O3 -march=native` – David Stone May 16 '12 at 14:30
  • 3
    The real reason it was included in C was because C does not have templates or function overloading, so they to create a differently-named function than just max for floating-point types. – David Stone Sep 01 '13 at 17:46
  • 1
    just tried this on g++4.8: fmax, `std::max` and even `(a>b)?a:b` all map to a single maxsd instruction on at -O1. (so you get different treatment of NaNs than at -O0...) – greggo Feb 03 '19 at 17:42
6

std::min and std::max are templates. So, they can be used on a variety of types that provide the less than operator, including floats, doubles, long doubles. So, if you wanted to write generic C++ code you'd do something like this:

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
}

As for performance, I don't think fmin and fmax differ from their C++ counterparts.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • 1
    What is ADL, and why do we want it here? – Rob Kennedy Oct 27 '09 at 17:29
  • 1
    ADL = argument dependent lookup. In this case it is probably not necessary because every user-defined type that comes with its own max-function is likely to provide a special less than operator as well. It's just a habit of mine that I write code like this -- primarily with `swap` and some numerical functions like `abs`. You would *want* to use a type's special swap and abs functions instead of the generic ones in case the special ones exist. I suggest reading Herb Sutter's article about "namespaces and the interface principle": http://www.gotw.ca/publications/mill08.htm – sellibitze Oct 28 '09 at 08:20
6

If your implementation provides a 64-bit integer type, you may get a different (incorrect) answer by using fmin or fmax. Your 64-bit integers will be converted to doubles, which will (at least usually) have a significand that's smaller than 64-bits. When you convert such a number to a double, some of the least significant bits can/will be lost completely.

This means that two numbers that were really different could end up equal when converted to double -- and the result will be that incorrect number, that's not necessarily equal to either of the original inputs.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
4

I would prefer the C++ min/max functions, if you are using C++, because they are type-specific. fmin/fmax will force everything to be converted to/from floating point.

Also, the C++ min/max functions will work with user-defined types as long as you have defined operator< for those types.

HTH

ecbrodie
  • 11,246
  • 21
  • 71
  • 120
Eric Melski
  • 16,432
  • 3
  • 38
  • 52
3

As Richard Corden pointed, use C++ functions min and max defined in std namespace. They provide type safety, and help to avoid comparing mixed types (i.e. float point vs integer) what sometimes may be undesirable.

If you find that C++ library you use defines min/max as macros as well, it may cause conflicts, then you can prevent unwanted macro substitution calling the min/max functions this way (notice extra brackets):

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

Remember, this will effectively disable Argument Dependant Lookup (ADL, also called Koenig lookup), in case you want to rely on ADL.

mloskot
  • 37,086
  • 11
  • 109
  • 136
2

As you noted yourself, fmin and fmax were introduced in C99. Standard C++ library doesn't have fmin and fmax functions. Until C99 standard library gets incorporated into C++ (if ever), the application areas of these functions are cleanly separated. There's no situation where you might have to "prefer" one over the other.

You just use templated std::min/std::max in C++, and use whatever is available in C.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

By the way, in cstdlib there are __min and __max you can use.

For more: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

Coding Mash
  • 3,338
  • 5
  • 24
  • 45
Justme0
  • 125
  • 1
  • 12
1

fmin and fmax are only for floating point and double variables.

min and max are template functions that allow comparison of any types, given a binary predicate. They can also be used with other algorithms to provide complex functionality.

Marcin
  • 12,245
  • 9
  • 42
  • 49
1

Use std::min and std::max.

If the other versions are faster then your implementation can add overloads for these and you'll get the benefit of performance and portability:

template <typename T>
T min (T, T) {
  // ... default
}

inline float min (float f1, float f2) {
 return fmin( f1, f2);
}    
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
1

Couldn't a C++ implementation targeted for processors with SSE instructions provide specializations of std::min and std::max for types float, double, and long double which do the equivalent of fminf, fmin, and fminl, respectively?

The specializations would provide better performance for floating-point types while the general template would handle non-floating-point types without attempting to coerce floating-point types into floating-point types that way the fmins and fmaxes would.

  • Intel c++ has better performance for std::min than fmin. In gcc, good performance of fmin requires finite-math-only setting which breaks it for non-finite operands. – tim18 Feb 04 '17 at 13:35
0

I always use the min and max macros for ints. I'm not sure why anyone would use fmin or fmax for integer values.

The big gotcha with min and max is that they're not functions, even if they look like them. If you do something like:

min (10, BigExpensiveFunctionCall())

That function call may get called twice depending on the implementation of the macro. As such, its best practice in my org to never call min or max with things that aren't a literal or variable.

popester
  • 1,936
  • 9
  • 13
  • 8
    min and max are often implemented as macros in C, but this is C++, where they're implemented as templates. Much, much better. – David Thornley Oct 27 '09 at 16:55
  • 4
    If you `#include `, you get `min` and `max` defined as macros. This will conflict with `std::min` and `std::max`, so you need to compile your sources with `#define NOMINMAX` to exclude the former. – Steve Guidi Oct 27 '09 at 17:16
  • 3
    It would have been nice if Microsoft put a `#ifdef _WINDOWS #undef min` in their `` header. Saves me the effort – MSalters Oct 28 '09 at 08:40
  • 2
    @MSalters: Good idea, however this is not the standard library's responsibility. They shouldn't have polluted the namespace with such common names instead. – the_drow Apr 10 '11 at 06:53
  • There is a weird gotcha with `std::min`: it actually accepts two const references, and returns one of them. Usually, that gets folded by the compiler. But I once had a `std::min( x, constval)`, where `constval` was defined as `static const int constval=10;` in the class. And, I got a link error: `undefined MyClass::constval`. Since, now the constval has to exist, since a reference to it is being taken. Can be fixed by using `std::min( x, constval+0)` – greggo Dec 07 '19 at 20:59
0

fmin and fmax, of fminl and fmaxl could be preferred when comparing signed and unsigned integers - you can take advantage of the fact that the entire range of signed and unsigned numbers and you don't have to worry about integer ranges and promotions.

unsigned int x = 4000000000;
int y = -1;

int z = min(x, y);
z = (int)fmin(x, y);
Eclipse
  • 44,851
  • 20
  • 112
  • 171