Использование функций min и max в C ++
Вопрос
Из C ++, являются min
и max
предпочтительнее, чем fmin
и fmax
?Для сравнения двух целых чисел предоставляют ли они в основном одинаковую функциональность?
Склонны ли вы использовать один из этих наборов функций или предпочитаете написать свой собственный (возможно, для повышения эффективности, переносимости, гибкости и т.д.)?
Примечания:
Стандартная библиотека шаблонов C ++ (STL) объявляет
min
иmax
функции в стандарте C ++ алгоритм заголовок.Стандарт C (C99) обеспечивает
fmin
иfmax
функция в стандарте C математика.h заголовок.
Заранее спасибо!
Решение
fmin
и fmax
специально для использования с числами с плавающей запятой (отсюда и " f "). Если вы используете его для целых чисел, вы можете потерять производительность или потерю точности из-за преобразования, издержек вызова функций и т. Д. В зависимости от вашего компилятора / платформы.
std::min
и std::max
- функции шаблонов (определенные в заголовке <algorithm>
), которые работают с любым типом с оператором меньше (<
), поэтому они могут работать с любым типом данных, который позволяет такое сравнение. Вы также можете предоставить свою собственную функцию сравнения, если не хотите, чтобы она работала <=>.
Это безопаснее, так как вам нужно явно преобразовать аргументы для соответствия, когда они имеют разные типы. Например, компилятор не позволит вам случайно преобразовать 64-битное int в 64-битное число с плавающей точкой. Одна только эта причина должна сделать шаблоны вашим выбором по умолчанию. (Благодарю Матье М & Amp; bk1e)
Даже при использовании с плавающей точкой шаблон может выиграть в производительности. Компилятор всегда имеет возможность встраивать вызовы шаблонных функций, поскольку исходный код является частью модуля компиляции. Иногда, невозможно встроить вызов библиотечной функции, с другой стороны (общие библиотеки, отсутствие оптимизации во время соединения и т. Д.).
Другие советы
Существует важное различие между 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
.
Вам не хватает всей точки fmin и fmax. Он был включен в C99, чтобы современные процессоры могли использовать свои собственные (читай SSE) инструкции для min и max с плавающей запятой и избегать тестов и ветвлений (и, следовательно, возможно ошибочно предсказанных ветвлений). Я переписал код, который использовал std :: min и std :: max, чтобы использовать встроенные функции SSE для min и max во внутренних циклах, а ускорение было значительным.
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
}
Что касается производительности, я не думаю, что fmin
и fmax
отличаются от своих аналогов в C ++.
Если ваша реализация предоставляет 64-битный целочисленный тип, вы можете получить другой (неправильный) ответ, используя fmin или fmax. Ваши 64-битные целые числа будут преобразованы в двойные, которые будут (по крайней мере, обычно) иметь значение, и это меньше, чем 64-битные. Когда вы конвертируете такое число в двойное, некоторые младшие биты могут / будут потеряны полностью.
Это означает, что два числа, которые действительно отличались друг от друга, могут оказаться равными при преобразовании в double - и в результате получится неправильное число, которое не обязательно будет равно ни одному из исходных входных данных.
Я бы предпочел функции min / max на C ++, если вы используете C ++, потому что они зависят от типа.fmin / fmax принудительно преобразует все данные в / из с плавающей запятой.
Кроме того, функции C ++ min / max будут работать с пользовательскими типами до тех пор, пока вы определили operator< для таких типов.
HTH
Как вы сами отметили, fmin
и fmax
были представлены в C99. Стандартная библиотека C ++ не имеет функций std::min
и std::max
. Пока стандартная библиотека C99 не будет включена в C ++ (если вообще когда-либо), области применения этих функций четко разделены. Там нет ситуации, когда вам, возможно, придется & Quot; предпочесть & Quot; один над другим.
Вы просто используете шаблонный <=> / <=> в C ++ и используете все, что доступно в C.
Как указал Ричард Корден, используйте функции C ++ min и max, определенные в пространстве имен std. Они обеспечивают безопасность типов и помогают избегать сравнения смешанных типов (то есть с плавающей точкой с целым числом), что иногда может быть нежелательным. Р>
Если вы обнаружите, что используемая вами библиотека C ++ также определяет min / max как макросы, это может вызвать конфликты, тогда вы можете предотвратить нежелательную замену макросов, вызывая функции min / max таким образом (обратите внимание на дополнительные скобки):
(std::min)(x, y)
(std::max)(x, y)
Помните, что это фактически отключит Аргумент-зависимый поиск (ADL, также называемый поиском Кенига) на тот случай, если вы хотите положиться на ADL.
fmin и fmax предназначены только для переменных с плавающей запятой и двойных.
min и max - это шаблонные функции, которые позволяют сравнивать любые типы по заданному двоичному предикату. Их также можно использовать с другими алгоритмами для обеспечения сложной функциональности.
Используйте std::min
и std::max
.
Если другие версии работают быстрее, ваша реализация может добавить к ним перегрузки, и вы получите преимущество в производительности и переносимости:
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}
Кстати, в cstdlib
есть __min
и __max
вы можете использовать.
Подробнее: http://msdn.microsoft.com/zh-cn /library/btkhtd8d.aspx р>
Не могла бы реализация C ++, предназначенная для процессоров с инструкциями SSE, обеспечить специализацию std:: мин и std:: макс для типов плавать, двойной, и длинный двойной которые выполняют эквивалент fminf, fmin, и fminl, соответственно?
Специализации обеспечили бы лучшую производительность для типов с плавающей запятой, в то время как общий шаблон обрабатывал бы типы с неплавающей запятой, не пытаясь принудительно преобразовать типы с плавающей запятой в типы с плавающей запятой, таким образом fminы и fmaxэс бы так и сделал.
Я всегда использую макросы min и max для целых. Я не уверен, почему кто-то использовал бы fmin или fmax для целочисленных значений.
Большая проблема с min и max в том, что они не являются функциями, даже если они похожи на них. Если вы делаете что-то вроде:
min (10, BigExpensiveFunctionCall())
Этот вызов функции может вызываться дважды в зависимости от реализации макроса. Поэтому в моей организации лучше никогда не вызывать min или max с вещами, которые не являются литералом или переменной.
fmin
и fmax
, из fminl
и fmaxl
могут быть предпочтительнее при сравнении целых чисел со знаком и без знака - вы можете воспользоваться тем, что весь диапазон чисел со знаком и без знака, и вы не нужно беспокоиться о целочисленных диапазонах и рекламных акциях.
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);