Использование функций min и max в С++

Из С++ предпочтительны min и max более fmin и fmax? Для сравнения двух целых чисел они обеспечивают в основном одну и ту же функциональность?

Как правило, вы используете один из этих наборов функций или предпочитаете писать свои собственные (возможно, для повышения эффективности, мобильности, гибкости и т.д.)?

Примечания:

  • Стандартная библиотека шаблонов С++ (STL) объявляет функции min и max в стандартном С++ algorithm заголовок.

  • В стандарте C (C99) предусмотрена функция fmin и fmax в стандартном заголовке C math.h.

Спасибо заранее!

Ответ 1

fmin и fmax специально предназначены для использования с числами с плавающей запятой (отсюда и "f" ). Если вы используете его для ints, вы можете потерять производительность или прецизионные потери из-за конверсии, накладных расходов функций и т.д. В зависимости от вашего компилятора/платформы.

std::min и std::max являются функциями шаблона (определены в заголовке <algorithm>), которые работают с любым типом с менее чем (<), поэтому они могут работать с любым типом данных, который допускает такое сравнение. Вы также можете предоставить свою собственную функцию сравнения, если вы не хотите, чтобы она работала <.

Это безопаснее, поскольку вы должны явно преобразовывать аргументы, чтобы они соответствовали друг другу, когда у них разные типы. Компилятор не позволит вам случайно преобразовать 64-битный int в 64-битный float, например. Только эта причина должна сделать шаблоны вашим выбором по умолчанию. (Кредит Matthieu M и bk1e)

Даже при использовании с поплавками шаблон может выигрывать в производительности. У компилятора всегда есть возможность встраивания вызовов в функции шаблона, поскольку исходный код является частью блока компиляции. Иногда невозможно встроить вызов библиотечной функции, с другой стороны (разделяемые библиотеки, отсутствие оптимизации времени соединения и т.д.).

Ответ 2

Существует важное различие между 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 для 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

тогда как для std::max(double, double) это просто

maxsd   xmm0, xmm1

Однако для GCC и Clang с использованием -Ofast fmax становится просто

maxsd   xmm0, xmm1

Итак, это еще раз показывает, что std::max является подмножеством fmax и что, когда вы используете более свободную модель с плавающей запятой, которая не имеет nan или имеет нулевой знак, тогда fmax и std::max являются одинаковыми, Тот же аргумент, очевидно, применим к fmin и std::min.

Ответ 3

Вам не хватает всей точки fmin и fmax. Он был включен в C99 так, чтобы современные процессоры могли использовать свои собственные инструкции (чтение SSE) для минимальных и максимальных значений с плавающей запятой и избегать тестирования и ветвления (и, следовательно, возможно, неправильно спрогнозированной ветки). Я переписал код, который использовал std:: min и std:: max, чтобы использовать встроенные функции SSE для min и max во внутренних циклах, и ускорение было значительным.

Ответ 4

std:: min и std:: max - это шаблоны. Таким образом, они могут использоваться на различных типах, которые обеспечивают меньше оператора, включая поплавки, удваивает, удваивают. Итак, если вы хотите написать общий код на С++, вы бы сделали что-то вроде этого:

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 отличаются от своих С++-аналогов.

Ответ 5

Если ваша реализация обеспечивает 64-разрядный целочисленный тип, вы можете получить другой (неверный) ответ, используя fmin или fmax. Ваши 64-битные целые числа будут преобразованы в двойные, что (как минимум, обычно) имеет значение, меньшее, чем 64-битные. Когда вы конвертируете такое число в double, некоторые из наименее значимых бит могут/будут полностью потеряны.

Это означает, что два числа, которые были действительно разными, могут быть равны при преобразовании в double - и результатом будет то неправильное число, которое не обязательно равно одному из исходных входов.

Ответ 6

Я бы предпочел функции С++ min/max, если вы используете С++, потому что они специфичны для типа. fmin/fmax заставит все преобразовать в/из плавающей точки.

Кроме того, функции С++ min/max будут работать с определенными пользователем типами, пока вы определили оператор < для этих типов.

НТН

Ответ 7

Как вы отметили, и fmax были введены на C99. Стандартная библиотека С++ не имеет функций fmin и fmax. До тех пор, пока стандартная библиотека C99 не будет включена в С++ (если вообще когда-либо), области приложений этих функций будут четко разделены. Там нет ситуации, когда вам, возможно, придется "отдать предпочтение" друг другу.

Вы просто используете templated std::min/std::max в С++ и используете все, что доступно на C.

Ответ 8

Как указал Ричард Корден, используйте функции С++ min и max, определенные в пространстве имен std. Они обеспечивают безопасность типов и помогают избежать сравнения смешанных типов (то есть точки с плавающей точкой и целого числа), что иногда может быть нежелательным.

Если вы обнаружите, что используемая вами библиотека С++ определяет min/max как макросы, это может вызвать конфликты, тогда вы можете предотвратить нежелательную макроподстановку, вызывающую функции min/max таким образом (обратите внимание на дополнительные скобки):

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

Помните, что это эффективно отключит "Поиск зависимых аргументов" (ADL, также называемый поиском Koenig), если вы хотите полагаться на ADL.

Ответ 9

fmin и fmax относятся только к плавающей запятой и двойным переменным.

min и max - это функции шаблона, которые позволяют сравнивать любые типы с учетом двоичного предиката. Они также могут использоваться с другими алгоритмами для обеспечения сложной функциональности.

Ответ 10

Используйте std::min и std::max.

Если другие версии быстрее, ваша реализация может добавить к ним перегрузки, и вы получите преимущество производительности и переносимости:

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

inline float min (float f1, float f2) {
 return fmin( f1, f2);
}    

Ответ 12

Невозможно, чтобы реализация С++, предназначенная для процессоров с инструкциями SSE, предоставляла специализации std:: min и std:: max для типов float, double и long double, которые эквивалентны fminf, fmin и fminl, соответственно?

Специализации обеспечивали бы лучшую производительность для типов с плавающей точкой, в то время как общий шаблон обрабатывал бы типы с плавающей точкой, не пытаясь принуждать типы с плавающей запятой к типам с плавающей точкой таким образом, чтобы fmin s и fmax es.

Ответ 13

Я всегда использую макросы min и max для ints. Я не уверен, почему кто-то будет использовать fmin или fmax для целых значений.

Большая добыча с min и max заключается в том, что они не являются функциями, даже если они выглядят как они. Если вы сделаете что-то вроде:

min (10, BigExpensiveFunctionCall())

Этот вызов функции может вызываться дважды в зависимости от реализации макроса. Таким образом, его лучшая практика в моей организации никогда не вызывает min или max с вещами, которые не являются буквальными или переменными.

Ответ 14

fmin и fmax, fminl и fmaxl могут быть предпочтительны при сравнении целых чисел без знака - вы можете воспользоваться тем фактом, что весь диапазон подписанных и неподписанных чисел и вы не должны беспокоиться о целых диапазонах и рекламных акциях.

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

int z = min(x, y);
z = (int)fmin(x, y);