Использование функций min и max в C ++ - PullRequest
63 голосов
/ 27 октября 2009

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

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

Примечания:

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

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

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

Ответы [ 14 ]

99 голосов
/ 27 октября 2009

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

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

Это безопаснее, поскольку вы должны явно преобразовывать аргументы для соответствия, когда они имеют разные типы. Например, компилятор не позволит вам случайно преобразовать 64-битное int в 64-битное число с плавающей точкой. Одна только эта причина должна сделать шаблоны вашим выбором по умолчанию. (Кредит Мэтью М & bk1e)

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

29 голосов
/ 18 июня 2015

Существует важное различие между 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
* * 1 028 * тогда +1029 *
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 использование -Ofast fmax становится просто

maxsd   xmm0, xmm1

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

16 голосов
/ 01 ноября 2010

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

6 голосов
/ 27 октября 2009

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

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

6 голосов
/ 27 октября 2009

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

4 голосов
/ 27 октября 2009

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

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

НТН

2 голосов
/ 24 января 2010

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

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

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

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

2 голосов
/ 27 октября 2009

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

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

1 голос
/ 10 февраля 2016

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

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

1 голос
/ 11 марта 2012

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

Для более: http://msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

...