C++から、min
およびmax
はfmin
およびfmax
よりも望ましいですか? 2つの整数を比較するために、それらは基本的に同じ機能を提供しますか?
これらの関数セットのいずれかを使用する傾向がありますか、それとも独自の関数を作成することを好みますか(おそらく、効率、移植性、柔軟性などを改善するために)。
注:
C++標準テンプレートライブラリ(STL)は、標準C++ アルゴリズム ヘッダーでmin
およびmax
関数を宣言します。
C標準(C99)は、標準C math.h ヘッダーでfmin
およびfmax
関数を提供します。
前もって感謝します!
fmin
およびfmax
は、特に浮動小数点数で使用するためのものです(したがって "f")。 intに使用すると、コンパイラ/プラットフォームによっては、変換、関数呼び出しのオーバーヘッドなどにより、パフォーマンスまたは精度が低下する場合があります。
std::min
およびstd::max
は、ヘッダー関数 <algorithm>
で定義されているテンプレート関数であり、より小さい(<
)演算子を含む任意の型で機能するため、このような比較を可能にする任意のデータ型で動作できます。 <
で機能させたくない場合は、独自の比較関数を提供することもできます。
引数が異なる型の場合、引数を明示的に一致するように変換する必要があるため、これはより安全です。コンパイラは、たとえば、64ビットintを誤って64ビットfloatに変換することを許可しません。この理由だけで、テンプレートをデフォルトの選択にする必要があります。 (Matthieu Mおよびbk1eへのクレジット)
Floatで使用した場合でも、テンプレートmayがパフォーマンスで勝ちます。ソースコードはコンパイル単位の一部であるため、コンパイラには常にテンプレート関数の呼び出しをインライン化するオプションがあります。一方で、ライブラリ関数の呼び出しをインライン化するのは、不可能(共有ライブラリ、リンク時最適化の欠如など)です。
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
はfmin
の1-1の代替ではありません。関数std::min
およびstd::max
は可換ではありません。 fmin
とfmax
のdoubleで同じ結果を得るには、引数を交換する必要があります
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はそれらを数学ライブラリから呼び出していることがわかります。 -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::max
がfmax
のサブセットであり、nan
または符号付きゼロを持たない緩い浮動小数点モデルを使用する場合、fmax
およびstd::max
は同じです。同じ引数がfmin
とstd::min
にも明らかに適用されます。
Fminとfmaxのポイント全体が欠落しています。最新のCPUがネイティブ(SSE読み取り)命令を浮動小数点の最小値と最大値に使用し、テストと分岐(したがって、予測ミスの可能性のある分岐)を回避できるように、C99に含まれていました。 std :: minとstd :: maxを使用して、内部ループでminとmaxの代わりにSSE組み込み関数を使用するコードを書き直し、スピードアップが大幅に向上しました。
std :: minおよびstd :: maxはテンプレートです。そのため、float、double、long doubleなど、小なり演算子を提供するさまざまなタイプで使用できます。したがって、一般的な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に変換すると、最下位ビットの一部が完全に失われる可能性があります。
つまり、実際に異なる2つの数値は、doubleに変換すると等しくなる可能性があり、結果はその誤った数値になり、元の入力のいずれかと必ずしも等しくなるわけではありません。
C++を使用している場合は、型固有であるため、C++のmin/max関数をお勧めします。 fmin/fmaxは、すべてを浮動小数点へ/から強制的に変換します。
また、C++のmin/max関数は、ユーザー定義型に対してoperator <を定義している限り、それらの型で機能します。
HTH
すでに述べたように、C99ではfmin
とfmax
が導入されました。標準C++ライブラリには、fmin
およびfmax
関数がありません。 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に依存したい場合は、これにより実質的に Argument Dependent Lookup (ADL、Koenigルックアップとも呼ばれます)が無効になります。
std::min
およびstd::max
を使用します。
他のバージョンがより高速である場合、実装はこれらのオーバーロードを追加でき、パフォーマンスと移植性の利点が得られます。
template <typename T>
T min (T, T) {
// ... default
}
inline float min (float f1, float f2) {
return fmin( f1, f2);
}
SSE命令を持つプロセッサを対象としたC++実装はできませんでした。std :: minおよびstd :: maxタイプfloat、double、およびlong doublefminf、fmin、およびfminl、それぞれ?
特殊化により、浮動小数点型のパフォーマンスが向上しますが、一般的なテンプレートは、浮動小数点型をfminsおよびfmaxesになります。
ところで、cstdlib
には__min
と__max
があります。
fminとfmaxは、浮動小数点変数とdouble変数専用です。
minおよびmaxは、バイナリ述語を指定すると、任意の型の比較を可能にするテンプレート関数です。また、他のアルゴリズムとともに使用して、複雑な機能を提供することもできます。
fmin
とfmax
のfminl
とfmaxl
は、符号付き整数と符号なし整数を比較するときに優先される可能性があります。整数の範囲とプロモーションについて心配する必要はありません。
unsigned int x = 4000000000;
int y = -1;
int z = min(x, y);
z = (int)fmin(x, y);
Intには常にminおよびmaxマクロを使用します。整数値にfminまたはfmaxを使用する理由がわからない。
Minとmaxの大きな落とし穴は、たとえそれらが見た目であっても、関数ではないということです。次のようなことをする場合:
min (10, BigExpensiveFunctionCall())
その関数呼び出しは、マクロの実装に応じて2回呼び出される場合があります。そのため、私の組織では、リテラルまたは変数ではないものでminまたはmaxを呼び出さないようにすることをお勧めします。