Использование функций min и max в С++
Из С++ предпочтительны min
и max
более fmin
и fmax
? Для сравнения двух целых чисел они обеспечивают в основном одну и ту же функциональность?
Как правило, вы используете один из этих наборов функций или предпочитаете писать свои собственные (возможно, для повышения эффективности, мобильности, гибкости и т.д.)?
Примечания:
Спасибо заранее!
Ответы
Ответ 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);
}
Ответ 11
Кстати, в cstdlib
есть __min
и __max
, которые вы можете использовать.
Подробнее: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx
Ответ 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);