簡體   English   中英

在 C++ 中使用 min 和 max 函數

[英]Use of min and max functions in C++

在 C++ 中, std::minstd::max優於fminfmax嗎? 對於比較兩個整數,它們是否提供基本相同的功能?

您傾向於使用這些函數集之一還是更喜歡編寫自己的函數(也許是為了提高效率、可移植性、靈活性等)?

筆記:

  1. C++ 標准模板庫 (STL) 在標准 C++算法頭文件中聲明minmax函數。

  2. C 標准 (C99) 在標准 C math.h標頭中提供fminfmax函數。

提前致謝!

fminfmax專門用於浮點數(因此稱為“f”)。 如果您將它用於整數,您可能會因轉換、函數調用開銷等而遭受性能或精度損失,具體取決於您的編譯器/平台。

std::minstd::max是模板函數(在頭文件<algorithm>定義),它們適用於具有小於 ( < ) 運算符的任何類型,因此它們可以對任何允許進行此類比較的數據類型進行操作。 如果您不想讓<起作用,您也可以提供自己的比較函數。

這更安全,因為當參數具有不同類型時,您必須顯式轉換參數以匹配。 例如,編譯器不會讓您意外地將 64 位 int 轉換為 64 位浮點數。 僅此原因就應該使模板成為您的默認選擇。 (歸功於 Matthieu M & bk1e)

即使與浮動一起使用,模板也可能在性能上獲勝。 編譯器始終可以選擇內聯對模板函數的調用,因為源代碼是編譯單元的一部分。 另一方面,有時不可能內聯對庫函數的調用(共享庫、缺少鏈接時優化等)。

std::minstd::maxfminfmax之間存在重要區別。

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不是fmin的 1-1 替代品。 函數std::minstd::max不是可交換的。 要使用fminfmax獲得相同的結果,應該交換參數

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 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::maxfmax的子集。

查看程序集表明,Clang 使用內置代碼來表示fmaxfmin而 GCC 從數學庫中調用它們。 帶有-O3 fmax clang 程序集是

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::maxfmax的子集,並且當您使用沒有nan或帶符號零的松散浮點模型時, fmaxstd::max是相同的。 同樣的論點顯然適用於fminstd::min

您錯過了 fmin 和 fmax 的全部內容。 它包含在 C99 中,以便現代 CPU 可以使用其原生(讀取 SSE)指令進行浮點最小值和最大值,並避免測試和分支(因此可能是錯誤預測的分支)。 我已經重新編寫了使用 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
}

至於性能,我認為fminfmax與它們的 C++ 對應物沒有區別。

如果您的實現提供 64 位整數類型,您可能會通過使用 fmin 或 fmax 得到不同(不正確)的答案。 您的 64 位整數將轉換為雙精度數,這將(至少通常)具有小於 64 位的有效數。 當您將這樣的數字轉換為雙精度數時,一些最低有效位可能/將完全丟失。

這意味着兩個真正不同的數字在轉換為 double 時最終可能相等——結果將是那個不正確的數字,它不一定等於原始輸入中的任何一個。

如果您使用 C++,我更喜歡 C++ min/max 函數,因為它們是特定於類型的。 fmin/fmax 將強制將所有內容轉換為浮點數/從浮點數轉換。

此外,只要您為這些類型定義了 operator<,C++ min/max 函數就可以使用用戶定義的類型。

HTH

正如您自己指出的那樣, fminfmax是在 C99 中引入的。 標准 C++ 庫沒有fminfmax函數。 在 C99 標准庫被並入 C++(如果有的話)之前,這些函數的應用領域是完全分開的。 在任何情況下,您都可能不得不“偏愛”一個而不是另一個。

您只需在 C++ 中使用模板化std::min / std::max ,並使用 C 中可用的任何內容。

正如 Richard Corden 所指出的,使用 std 命名空間中定義的 C++ 函數 min 和 max。 它們提供類型安全,並有助於避免比較混合類型(即浮點與整數)有時可能是不受歡迎的。

如果你發現你使用的 C++ 庫也將 min/max 定義為宏,它可能會導致沖突,那么你可以防止不需要的宏替換以這種方式調用 min/max 函數(注意額外的括號):

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

請記住,這將有效地禁用參數相關查找(ADL,也稱為 Koenig 查找),以防您想依賴 ADL。

fmin 和 fmax 僅適用於浮點和雙變量。

min 和 max 是允許比較任何類型的模板函數,給定一個二元謂詞。 它們還可以與其他算法一起使用以提供復雜的功能。

使用std::minstd::max

如果其他版本更快,那么您的實現可以為這些添加重載,您將獲得性能和可移植性的好處:

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

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

針對具有 SSE 指令的處理器的 C++ 實現不能為floatdoublelong double類型提供std::minstd::max 的特化,它們分別相當於fminffminfminl嗎?

特化將為浮點類型提供更好的性能,而通用模板將處理非浮點類型,而不會像fmin s 和fmax es 那樣試圖將浮點類型強制轉換為浮點類型。

順便說一下,在cstdlib__min__max你可以使用。

更多信息: http : //msdn.microsoft.com/zh-cn/library/btkhtd8d.aspx

我總是對整數使用 min 和 max 宏。 我不確定為什么有人會使用 fmin 或 fmax 作為整數值。

min 和 max 的最大問題是它們不是函數,即使它們看起來像它們。 如果您執行以下操作:

min (10, BigExpensiveFunctionCall())

根據宏的實現,該函數調用可能會被調用兩次。 因此,它在我的組織中的最佳實踐是永遠不要使用不是文字或變量的東西調用 min 或 max。

在比較有符號和無符號整數時, fminlfmaxl fminfmax可能是首選 - 您可以利用整個范圍的有符號和無符號數字這一事實,而不必擔心整數范圍和提升。

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM