2009-10-27 168 views
57

Từ C++, là minmax thích hợp hơn fminfmax? Để so sánh hai số nguyên, chúng có cung cấp các chức năng cơ bản giống nhau không?Sử dụng các hàm min và max trong C++

Bạn có xu hướng sử dụng một trong các bộ chức năng này hay bạn muốn tự viết (có lẽ để nâng cao hiệu quả, tính di động, tính linh hoạt, v.v.)?

Ghi chú:

  1. C++ Standard Template Library (STL) tuyên bố các chức năng minmax trong chuẩn C++ algorithm tiêu đề.

  2. Chuẩn C (C99) cung cấp hàm fminfmax trong tiêu đề C math.h tiêu chuẩn.

Cảm ơn bạn trước!

Trả lời

86

fminfmax được sử dụng đặc biệt với số dấu phẩy động (do đó là "f"). Nếu bạn sử dụng nó cho ints, bạn có thể bị mất hiệu suất hoặc chính xác do chuyển đổi, chức năng gọi trên không, vv tùy thuộc vào trình biên dịch/nền tảng của bạn.

std::minstd::max là hàm mẫu (được định nghĩa trong tiêu đề <algorithm>) mà làm việc trên bất kỳ loại với một ít hơn (<) điều hành, vì vậy họ có thể hoạt động trên bất kỳ kiểu dữ liệu cho phép một sự so sánh như vậy. Bạn cũng có thể cung cấp chức năng so sánh của riêng mình nếu bạn không muốn nó hoạt động <.

Điều này là an toàn hơn vì bạn phải chuyển đổi rõ ràng các đối số để khớp khi chúng có các loại khác nhau. Trình biên dịch sẽ không cho phép bạn vô tình chuyển đổi một int 64-bit thành một phao 64-bit, ví dụ. Lý do này một mình nên làm cho các mẫu của bạn lựa chọn mặc định. (Credit để Matthieu M & bk1e)

Ngay cả khi được sử dụng với phao mẫu thể giành chiến thắng trong hoạt động. Trình biên dịch luôn có tùy chọn nội tuyến các cuộc gọi đến các hàm mẫu vì mã nguồn là một phần của đơn vị biên dịch. Đôi khi, không thể để nội tuyến cuộc gọi đến chức năng thư viện, mặt khác (thư viện được chia sẻ, thiếu tối ưu hóa thời gian liên kết, v.v.).

+7

Cảnh báo: tối thiểu và tối đa chỉ có thể so sánh hai biến của cùng một loại chính xác ... vì vậy bạn không thể so sánh số int và số double với chúng: ( –

+3

True - max (1, 2.0) không hoạt động, nó có để được một cái gì đó như tối đa (1, 2.0) hoặc tối đa (gấp đôi (1), 2.0) –

+39

Đó là một điều tốt ™ IMO :) – Cogwheel

0

Tôi luôn sử dụng macro tối thiểu và tối đa cho int. Tôi không chắc chắn lý do tại sao bất cứ ai sẽ sử dụng fmin hoặc fmax cho các giá trị số nguyên.

Bản ghi nhớ lớn với tối thiểu và tối đa là chúng không hoạt động, ngay cả khi chúng trông giống như chúng. Nếu bạn làm điều gì đó như:

min (10, BigExpensiveFunctionCall()) 

Cuộc gọi chức năng đó có thể được gọi hai lần tùy thuộc vào việc triển khai macro. Như vậy, thực hành tốt nhất của nó trong tổ chức của tôi không bao giờ gọi min hoặc max với những thứ không phải là chữ hoặc biến.

+8

std :: min không phải là macro ... – Cogwheel

+6

phút và tối đa thường được triển khai dưới dạng macro trong C, nhưng đây là C++, nơi chúng được triển khai dưới dạng mẫu. Nhiều, tốt hơn nhiều. –

+3

Nếu bạn '#include ', bạn nhận được 'min' và' max' được định nghĩa là macro. Điều này sẽ xung đột với 'std :: min' và' std :: max', vì vậy bạn cần phải biên dịch các nguồn của bạn bằng '#define NOMINMAX' để loại bỏ phần tử cũ. –

4

Tôi thích hàm C/min tối đa của C++, nếu bạn đang sử dụng C++, vì chúng thuộc loại cụ thể. fmin/fmax sẽ buộc tất cả mọi thứ được chuyển đổi thành/từ dấu phẩy động.

Ngoài ra, chức năng tối thiểu/tối đa C++ sẽ hoạt động với các loại do người dùng xác định miễn là bạn đã xác định toán tử < cho các loại đó.

HTH

6

std :: min và std :: max là các mẫu. Vì vậy, chúng có thể được sử dụng trên nhiều loại cung cấp cho người vận hành ít hơn, bao gồm phao, đôi, đôi dài. Vì vậy, nếu bạn muốn viết chung C++ bạn muốn làm một cái gì đó như thế này:

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 
} 

Đối với hiệu suất, tôi không nghĩ rằng fminfmax khác với C++ đối tác của họ.

+1

ADL là gì và tại sao chúng ta muốn nó ở đây? –

+0

@Rob Kennedy: http: //en.wikipedia.org/wiki/Argument_dependent_name_lookup – bk1e

+1

ADL = tra cứu phụ thuộc đối số. Trong trường hợp này, có thể không cần thiết vì mọi loại do người dùng định nghĩa đi kèm với hàm tối đa riêng của nó cũng có khả năng cung cấp một toán tử đặc biệt nhỏ hơn. Nó chỉ là một thói quen của tôi mà tôi viết mã như thế này - chủ yếu với 'swap' và một số hàm số như' abs'. Bạn sẽ * muốn * sử dụng các chức năng hoán đổi và abs đặc biệt của một loại thay vì các hàm chung chung trong trường hợp những cái đặc biệt tồn tại. Tôi khuyên bạn nên đọc bài viết của Herb Sutter về "không gian tên và nguyên tắc giao diện": http://www.gotw.ca/publications/mill08.htm – sellibitze

1

fmin và fmax chỉ dành cho các điểm động và biến kép.

phút và tối đa là các hàm mẫu cho phép so sánh bất kỳ loại nào, được cung cấp một vị từ nhị phân. Chúng cũng có thể được sử dụng với các thuật toán khác để cung cấp chức năng phức tạp.

2

Như bạn đã lưu ý, fminfmax đã được giới thiệu trong C99. Thư viện chuẩn C++ không có các hàm fminfmax. Cho đến khi thư viện chuẩn C99 được tích hợp vào C++ (nếu bao giờ), các vùng ứng dụng của các hàm này được tách riêng biệt. Không có tình huống mà bạn có thể phải "thích" hơn cái kia.

Bạn chỉ cần sử dụng templated std::min/std::max trong C++, và sử dụng bất cứ điều gì có sẵn trong C.

6

Nếu thực hiện của bạn cung cấp một kiểu số nguyên 64-bit, bạn có thể nhận được một (không chính xác) câu trả lời khác nhau bằng cách sử dụng fmin hoặc fmax. Số nguyên 64 bit của bạn sẽ được chuyển đổi thành gấp đôi, điều này sẽ ít nhất là 64 bit. Khi bạn chuyển đổi một số như vậy thành một đôi, một số bit ít quan trọng nhất có thể/sẽ bị mất hoàn toàn. Điều này có nghĩa là hai số thực sự khác nhau có thể kết thúc bằng nhau khi được chuyển đổi thành gấp đôi - và kết quả sẽ là số không chính xác, điều đó không nhất thiết phải bằng một trong hai yếu tố đầu vào ban đầu.

0

fminfmax, của fminlfmaxl có thể được ưu tiên khi so sánh số nguyên unsigned ký và - bạn có thể tận dụng thực tế là toàn bộ phạm vi các số có chữ ký và unsigned và bạn không phải lo lắng về dãy số nguyên và chương trình khuyến mãi.

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

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

tại sao không có chuyên môn xử lý các trường hợp này? –

1

Sử dụng std::minstd::max.

Nếu các phiên bản khác là nhanh hơn sau đó thực hiện của bạn có thể thêm quá tải cho những điều này và bạn sẽ nhận được những lợi ích về hiệu suất và tính di động:

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

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

Như Richard Corden chỉ, sử dụng C chức năng ++ min và max quy định tại không gian tên std. Chúng cung cấp an toàn loại và giúp tránh so sánh các loại hỗn hợp (ví dụ: điểm phao so với số nguyên) đôi khi có thể không mong muốn.

Nếu bạn thấy rằng C++ thư viện bạn sử dụng định nghĩa min/max như macro là tốt, nó có thể gây ra xung đột, sau đó bạn có thể ngăn chặn thay vĩ mô không mong muốn kêu gọi các min/chức năng tối đa theo cách này (thông báo ngoặc thêm):

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

Hãy nhớ, điều này sẽ vô hiệu hóa hiệu quả Argument Dependant Lookup (ADL, còn được gọi là tra cứu Koenig), trong trường hợp bạn muốn dựa vào ADL.

13

Bạn đang thiếu toàn bộ điểm của fmin và fmax. Nó được bao gồm trong C99 để các CPU hiện đại có thể sử dụng các chỉ lệnh gốc (đọc SSE) của chúng cho điểm trôi nổi và tối đa và tránh kiểm tra và nhánh (và do đó có thể là một nhánh dự đoán sai). Tôi đã viết lại mã sử dụng std :: min và std :: max để sử dụng nội tại SSE cho min và max trong vòng lặp thay vào đó thay vào đó và tăng tốc là đáng kể.

+1

Tốc độ tăng tốc là bao nhiêu? Tại sao trình biên dịch C++ không thể phát hiện khi bạn đang sử dụng std :: min ? –

+4

Có lẽ anh ta không bật tối ưu hóa khi kiểm tra, nếu không trình biên dịch đang cố gắng biên dịch một tệp nhị phân có thể chạy 'ở bất cứ đâu' và do đó không biết nó có thể sử dụng SSE. Tôi nghi ngờ rằng bằng cách sử dụng gcc, sự khác biệt sẽ biến mất nếu bạn vượt qua các cờ '-O3 -march = native' –

+2

Lý do thực sự nó được bao gồm trong C là vì C không có mẫu hoặc quá tải hàm, do đó, chúng tạo ra một cách khác nhau- chức năng được đặt tên hơn là chỉ tối đa cho các loại dấu phẩy động. –

14

Có một sự khác biệt quan trọng giữa std::min, std::maxfminfmax.

std::min(-0.0,0.0) = -0.0 
std::max(-0.0,0.0) = -0.0 

trong khi

fmin(-0.0, 0.0) = -0.0 
fmax(-0.0, 0.0) = 0.0 

Vì vậy std::min không thể thay thế 1-1 cho fmin. Các hàm std::minstd::max không giao hoán. Để có được kết quả tương tự với đôi với fminfmax ta nên trao đổi các đối số

fmin(-0.0, 0.0) = std::min(-0.0, 0.0) 
fmax(-0.0, 0.0) = std::max(0.0, -0.0) 

Nhưng như xa như tôi có thể nói all these functions are implementation defined anyway in this case như vậy để chắc chắn 100% bạn phải kiểm tra cách thức chúng được thực hiện.


Có một sự khác biệt quan trọng. Đối với x ! = NaN:

std::max(Nan,x) = NaN 
std::max(x,NaN) = x 
std::min(Nan,x) = NaN 
std::min(x,NaN) = x 

trong khi

fmax(Nan,x) = x 
fmax(x,NaN) = x 
fmin(Nan,x) = x 
fmin(x,NaN) = x 

fmax có thể được mô phỏng với đoạn mã sau

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); 
} 

Điều này cho thấy std::max là một tập hợp con của fmax.

Nhìn vào hội đồng cho thấy rằng Clang sử dụng mã dựng sẵn cho fmaxfmin trong khi GCC gọi chúng từ thư viện toán học.Việc lắp ráp cho vang cho fmax với -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 

trong khi cho std::max(double, double) nó chỉ đơn giản là

maxsd xmm0, xmm1 

Tuy nhiên, đối với GCC và Clang sử dụng -Ofastfmax đơn giản trở nên

maxsd xmm0, xmm1 

Vì vậy, chương trình này một lần nữa rằng std::max là một tập con của fmax và khi bạn sử dụng một mô hình điểm động lỏng lẻo không có số nan hoặc số 0 thì fmaxstd::max là giống nhau. Cùng một đối số rõ ràng áp dụng cho fminstd::min.

0

thể không phải là một C++ thực hiện mục tiêu cho bộ vi xử lý với các chỉ lệnh SSE cung cấp chuyên ngành của std :: phútstd :: max với nhiều loại phao, đôilong double mà làm tương đương với fminf, fminfminl, tương ứng?

Các chuyên ngành sẽ cung cấp hiệu suất tốt hơn với nhiều loại dấu chấm động trong khi mẫu chung sẽ xử lý các loại phi floating-point mà không cố gắng ép buộc kiểu dấu chấm động thành các loại dấu chấm động rằng cách thức fmin s và fmax es.

+0

Intel C++ có hiệu suất tốt hơn cho std :: min so với fmin. Trong gcc, hiệu năng tốt của fmin yêu cầu thiết lập chỉ có tính toán hữu hạn, nó phá vỡ nó cho các toán hạng không có giới hạn. – tim18

Các vấn đề liên quan