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

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

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

Заметки:

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

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

Заранее спасибо!

14 ответов

Решение

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

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

Это безопаснее, поскольку вы должны явно преобразовывать аргументы в соответствие, когда они имеют разные типы. Например, компилятор не позволит вам случайно преобразовать 64-битное int в 64-битное число с плавающей точкой. Одна только эта причина должна сделать шаблоны вашим выбором по умолчанию. (Кредит Мэтью М & 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 вызывает их из математической библиотеки. Сборка для лязга для 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 используется -Ofastfmax становится просто

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-битные. Когда вы конвертируете такое число в двойное, некоторые из младших битов могут / будут потеряны полностью.

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

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

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

НТН

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

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

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

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

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

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

Кстати, в cstdlib имеются __min а также __max ты можешь использовать.

Для получения дополнительной информации: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

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

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

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

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

Использование std::min а также std::max,

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

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

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

Я всегда использую макросы 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);
Другие вопросы по тегам