bporter
bporter

Reputation: 3652

What is the difference between std::min/std::max and fmin/fmax?

In 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 (also provided in C++11 as std::fmin and std::fmax in the cmath header).

Upvotes: 89

Views: 356608

Answers (16)

Z boson
Z boson

Reputation: 33709

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)
{
    // after the next two comparisons, x and y are equivalent or there is NaN
    if(x < y)           return y;
    if(y < x)           return x;
    // z > nan for z != nan is required by C the standard
    if(std::isnan(x))   return y;
    if(std::isnan(y))   return x;
    // +0 > -0 is preferred by C the standard
    if(std::signbit(x)) return y;
    else                return x;
}

std::max is a subset of fmax because it only needs the first comparison (x < y).

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.

Upvotes: 56

saikat bhattacharya
saikat bhattacharya

Reputation: 1

The key difference is in the types of values they operate on. std::min and std::max are more general and can be used with various data types, while fmin and fmax are specifically designed for floating-point numbers. If you are working with integers or other types, you should use std::min and std::max.

Upvotes: -1

Jan Schultke
Jan Schultke

Reputation: 40199

In short, std::max is a "generic function" from the <algotithm> library that simply chooses the lower of two arguments according to the < operator, whereas std::fmax is specific to floating-point numbers and treats NaN as missing data.

std::fmax/std::fmin

The contents and meaning of the header <cmath> are the same as the C standard library header <math.h> [...]

- C++23 standard : [cmath.syn] p1

If just one argument is a NaN, the fmax functions return the other argument (if both arguments are NaNs, the functions return a NaN).
[...]
The body of the fmax function might be

{ return (isgreaterequal(x, y) || isnan(y)) ? x : y; }

- C17 standard: F.10.9.2 The fmax functions

It's also worth noting that fmax/fmin may be sensitive to the sign of zero:

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.

- C17 standard: footnote 380

std::max/std::min

template<class T>
constexpr const T& max(const T& a, const T& b);
// [...]

Preconditions: For the first form, T meets the Cpp17LessThanComparable requirements (Table 29).

Returns: The larger value. Returns the first argument when the arguments are equivalent.

- [alg.min.max] std::max

There is no sample implementation in the standard, but a possible implementation looks like:

{ return a < b ? b : a; }

Which one to choose

For performance reasons, it is better to use std::max and std::min. On modern x86_64, std::max compiles to a single instruction

vmaxss  xmm0, xmm1, xmm0            ; xmm0 = max(xmm1, xmm0)

... whereas std::fmax compiles to

vmaxss      xmm2, xmm1, xmm0        ; xmm2 = max(xmm1, xmm0)
vcmpunordss xmm0, xmm0, xmm0        ; test xmm0 for NaN, set mask bits
vblendvps   xmm0, xmm2, xmm1, xmm0  ; xmm0 = isnan(xmm0) ? xmm1 : xmm2

See Compiler Explorer.

If there's no possibility of operands being NaN, you should go for std::max/std::min. Otherwise, use std::fmax/std::fmin.

Further Notes on Assembly

std::fmax(x, y) cannot be implemented using only vmaxss dest, y, x because if x or y is NaN, dest = x. std::fmax chooses x or y depending on which one isn't NaN. vmaxss is biased towards x in every possible way.

However, vmaxss matches the behavior of x < y ? y : x and thus std::max exactly.

Upvotes: 0

Cogwheel
Cogwheel

Reputation: 23237

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.).

Upvotes: 123

Michael Aramini
Michael Aramini

Reputation: 185

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.

Upvotes: 0

Eric Melski
Eric Melski

Reputation: 16840

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

Upvotes: 4

Justme0
Justme0

Reputation: 133

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

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

Upvotes: 1

J Ridges
J Ridges

Reputation: 181

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.

Upvotes: 18

mloskot
mloskot

Reputation: 38962

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.

Upvotes: 3

Richard Corden
Richard Corden

Reputation: 21731

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

Upvotes: 0

Eclipse
Eclipse

Reputation: 45533

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

Upvotes: -1

Jerry Coffin
Jerry Coffin

Reputation: 490693

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.

Upvotes: 6

AnT stands with Russia
AnT stands with Russia

Reputation: 320777

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.

Upvotes: 2

Marcin
Marcin

Reputation: 12620

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.

Upvotes: 0

sellibitze
sellibitze

Reputation: 28127

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.

Upvotes: 6

popester
popester

Reputation: 1934

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.

Upvotes: -1

Related Questions