MIN i MAX w C

299

Gdzie są MINi MAXzdefiniowane w C, jeśli w ogóle?

Jaki jest najlepszy sposób na ich wdrożenie, tak ogólnie i bezpiecznie, jak to możliwe? (Preferowane są rozszerzenia / wbudowane kompilatory kompilatorów głównego nurtu).

Matt Joiner
źródło

Odpowiedzi:

392

Gdzie są MINi MAXzdefiniowane w C, jeśli w ogóle?

Nie są.

Jaki jest najlepszy sposób na ich wdrożenie, tak ogólnie, jak to tylko możliwe i jak najbardziej bezpieczne (preferowane są rozszerzenia / wbudowane kompilatory dla głównych kompilatorów).

Jako funkcje. Nie używałbym takich makr #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), zwłaszcza jeśli planujesz wdrożyć swój kod. Napisz własny, użyj czegoś standardowego fmaxlub fminpopraw makro za pomocą GFF typeof (dostajesz premię za bezpieczeństwo typów) w wyrażeniu instrukcji GCC :

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

Wszyscy mówią „och, wiem o podwójnej ocenie, to nie problem”, a kilka miesięcy później będziesz debugować najgłupsze problemy przez wiele godzin.

Zwróć uwagę na użycie __typeof__zamiast typeof:

Jeśli piszesz plik nagłówkowy, który musi działać po włączeniu do programów ISO C, pisz __typeof__zamiast typeof.

David Titarenco
źródło
68
Wiesz, byłoby całkiem użyteczne, gdyby gcc miał ostrzeżenie w stylu: warning: expression with side-effects multiply evaluated by macrow punkcie użycia ...
caf
23
@caf: czy nie wymagałoby to od preprocesora bardziej skomplikowanej znajomości składni języka C?
dreamlax
3
Po wielu próbach odgadnięcia, nie sądzę, aby było to możliwe w VC ++, ale najlepiej jest spróbować zadziałać z nowym decltypesłowem kluczowym MSVC ++ 2010 - ale mimo to Visual Studio nie może wykonywać instrukcji złożonych w makrach (i decltypetak czy inaczej to C ++), tj. ({ ... })składnia GCC, więc jestem prawie pewien, że i tak nie jest to możliwe. Nie patrzyłem na żadne inne kompilatory dotyczące tego problemu, przepraszam Luther: S
David Titarenco
7
@dreamlax Raz widziałem przypadek, w którym ktoś zrobił, MAX(someUpperBound, someRandomFunction())aby ograniczyć losową wartość do pewnego górnego limitu. To był okropny pomysł, ale też nie zadziałał, ponieważ ten, MAXktórego używał, miał problem z podwójną oceną, więc skończył z inną losową liczbą niż ta, którą początkowo oceniano.
Zev Eisenberg,
8
@Soumen Na przykład, jeśli zadzwonisz, MIN(x++, y++)preprocesor wygeneruje następujący kod (((x++) < (y++)) ? (x++) : (y++)). Tak, xi ybędą zwiększane dwukrotnie.
Antonio
91

Jest on także dostępny w wersjach sys / param.h GNU libc (Linux) i FreeBSD i ma definicję dostarczoną przez dreamlax.


W systemie Debian:

$ uname -sr
Linux 2.6.11

$ cat /etc/debian_version
5.0.2

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.

Na FreeBSD:

$ uname -sr
FreeBSD 5.5-STABLE

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

Repozytoria źródłowe są tutaj:

Mikel
źródło
Dodałem definicje z systemów, do których mam dostęp w mojej odpowiedzi powyżej (pole komentarza nie akceptuje formatowania, o ile mi wiadomo). Spróbuje znaleźć linki do repozytoriów źródłowych FreeBSD / Linux / glibc.
Mikel
+1. Bardzo dobrze. Działa dla openSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) też. :) Źle, to nie jest przenośne.
Jack
Działa również na Cygwin.
CMCDragonkai
1
Poczekaj chwilę. Nie zapobiega podwójnej ocenie, prawda? : 3
użytkownik1857492
76

Jest std::mini std::maxw C ++, ale AFAIK, nie ma odpowiednika w bibliotece standardowej C. Możesz je zdefiniować samodzielnie za pomocą makr takich jak

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

Ale to powoduje problemy, jeśli napiszesz coś takiego MAX(++a, ++b).

dan04
źródło
10
po co stawiać za dużo nawiasów ??? Znalazłem quiz, w którym powiedzieli, że #define MIN(A, B) ((A < B) ? A : B)nie jest elastyczny sposób, dlaczego ???
78
@Makouda: dodatkowe nawiasy w makrach pomagają uniknąć problemów z pierwszeństwem operatora. Rozważmy na przykład #define MULT(x, y) x * y. Następnie MULT(a + b, a + b)rozwija się do a + b * a + b, który analizuje jako a + (b * a) + bze względu na pierwszeństwo. Prawdopodobnie nie tak zamierzał programista.
dan04
które nie są potrzebne, kiedy?: ma zresztą najniższy priorytet
Winger Sendon,
@WingerSendon: Nie; robi przecinek.
dan04
24

Unikaj niestandardowych rozszerzeń kompilatora i zaimplementuj je jako całkowicie bezpieczne makro typu w czystym standardzie C (ISO 9899: 2011).

Rozwiązanie

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

Stosowanie

MAX(int, 2, 3)

Wyjaśnienie

Makro MAX tworzy kolejne makro na podstawie typeparametru. To makro sterujące, jeśli zostało zaimplementowane dla danego typu, służy do sprawdzania, czy oba parametry są poprawnego typu. Jeśli typenie jest obsługiwany, wystąpi błąd kompilatora.

Jeśli x lub y nie jest poprawnego typu, w ENSURE_makrach wystąpi błąd kompilatora . Więcej takich makr można dodać, jeśli obsługiwanych jest więcej typów. Założyłem, że będą używane tylko typy arytmetyczne (liczby całkowite, zmiennoprzecinkowe, wskaźniki itp.), A nie struktury i tablice itp.

Jeśli wszystkie typy są poprawne, makro GENERIC_MAX zostanie wywołane. Potrzebne są dodatkowe nawiasy wokół każdego parametru makra, jako zwykłe standardowe środki ostrożności podczas pisania makr C.

Potem są zwykłe problemy z niejawnymi promocjami typu w C. ?:Operator równoważy drugi i trzeci operand względem siebie. Na przykład wynikiem GENERIC_MAX(my_char1, my_char2)może być int. Aby zapobiec wykonywaniu potencjalnie niebezpiecznych promocji przez makro, zastosowano ostateczny rzut rzutowany na zamierzony typ.

Racjonalne uzasadnienie

Chcemy, aby oba parametry makra były tego samego typu. Jeśli jedno z nich jest innego typu, makro nie jest już bezpieczne, ponieważ operator lubi?: zapewni awans typu pośredniego. A ponieważ tak jest, zawsze musimy rzutować końcowy wynik z powrotem na zamierzony typ, jak wyjaśniono powyżej.

Makro z tylko jednym parametrem mogłoby zostać napisane w znacznie prostszy sposób. Ale przy 2 lub więcej parametrach konieczne jest dołączenie dodatkowego parametru typu. Ponieważ coś takiego jest niestety niemożliwe:

// this won't work
#define MAX(x, y)                                  \
  _Generic((x),                                    \
           int: GENERIC_MAX(x, ENSURE_int(y))      \
           float: GENERIC_MAX(x, ENSURE_float(y))  \
          )

Problem polega na tym, że jeśli powyższe makro zostanie wywołane tak jak MAX(1, 2)dwaint , nadal będzie próbowało rozwinąć makro wszystkie możliwe scenariusze z _Genericlisty skojarzeń. Więc ENSURE_floatmakro będzie się zbyt rozbudowany, choć nie dotyczy int. A ponieważ to makro celowo zawiera tylko floattyp, kod nie zostanie skompilowany.

Aby rozwiązać ten problem, utworzyłem nazwę makra podczas fazy preprocesora, za pomocą operatora ##, aby żadne makro nie zostało przypadkowo rozwinięte.

Przykłady

#include <stdio.h>

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

int main (void)
{
  int    ia = 1,    ib = 2;
  float  fa = 3.0f, fb = 4.0f;
  double da = 5.0,  db = 6.0;

  printf("%d\n", MAX(int,   ia, ib)); // ok
  printf("%f\n", MAX(float, fa, fb)); // ok

//printf("%d\n", MAX(int,   ia, fa));  compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib));  compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db));  compiler error, one of the types is wrong

//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
  return 0;
}
Lundin
źródło
GENERIC_MAXNawiasem mówiąc, to makro jest złym pomysłem, musisz tylko spróbować GENERIC_MAX(var++, 7)dowiedzieć się, dlaczego :-) W dzisiejszych czasach (szczególnie przy mocno kompilujących / optymalizujących kompilatorach) makra powinny być w zasadzie sprowadzane tylko do prostych formularzy. Funkcje podobne są lepsze jako funkcje, a grupy wartości lepsze jako wyliczenia.
paxdiablo
21

Nie sądzę, że są to standardowe makra. Istnieją już znormalizowane funkcje dla liczb zmiennoprzecinkowych fmaxi fmin(oraz dla liczb zmiennoprzecinkowych fmaxfi fmaxldługich podwójnych).

Możesz je zaimplementować jako makra, o ile masz świadomość problemów związanych z efektami ubocznymi / podwójną oceną.

#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)

W większości przypadków możesz pozostawić to kompilatorowi, aby określić, co próbujesz zrobić, i zoptymalizować go najlepiej, jak to możliwe. Chociaż powoduje to problemy przy takim użyciu MAX(i++, j++), wątpię, aby zawsze była potrzeba sprawdzania maksymalnej wartości przyrostowej za jednym razem. Najpierw zwiększ, a następnie sprawdź.

dreamlax
źródło
Powinno to być korzystne rozwiązanie, ponieważ nie są wyraźnie min i max funkcje w bibliotece matematycznej: cplusplus.com/reference/cmath/fmax
imranal
@imranal O czym dokładnie mówisz? Realizacja Kod z tych bibliotek? Ale ten kod nie jest ujawniony , tzn. Nie umieszcza go w interfejsie biblioteki, ponieważ jest potencjalnie niebezpieczny.
Antonio
@Antonio Myślę, że używasz niepoprawnych definicji „narażonych” i „interfejsu”. Interfejsem biblioteki ac są zewnętrzne zmienne, typy, makra i deklaracje funkcji w pliku nagłówkowym; fmin / fmax są zadeklarowane w pliku nagłówkowym, więc mówi się, że są ujawnione. Nie jestem jednak pewien, co nazywasz niebezpiecznym.
rationalcoder
21

To późna odpowiedź z powodu dość niedawnego rozwoju. Ponieważ PO zaakceptował odpowiedź, która opiera się na nieprzenośnym rozszerzeniu GCC (i clang) typeof- lub __typeof__na „czystym” ISO C - dostępne jest lepsze rozwiązanie od gcc-4.9 .

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

Oczywistą zaletą tego rozszerzenia jest to, że każdy argument makra jest rozszerzany tylko raz, w przeciwieństwie do __typeof__rozwiązania.

__auto_typejest ograniczoną formą C ++ 11 auto. Nie można go (lub nie należy?) Używać w kodzie C ++, choć nie ma żadnego dobrego powodu, aby nie korzystać z lepszych możliwości wnioskowania typuauto przypadku używania C ++ 11.

To powiedziawszy, zakładam , że nie ma problemów z użyciem tej składni, gdy makro jest zawarte w extern "C" { ... }zakresie; np. z nagłówka C. AFAIK, to rozszerzenie nie znalazło sposobu na kliknięcie informacji

Brett Hale
źródło
Związany z komentarzem Bretta Hale'a , clangzaczął wspierać __auto_typeokoło 2016 roku (patrz łatka ).
Lars,
Wyrazy uznania za rozpoznanie problemu z makrem, ale nadal uważam, że funkcja prawdopodobnie byłaby lepsza :-)
paxdiablo
@paxdiablo - Zgadzam się, choć pytanie ma c-preprocessortag. Nie można zagwarantować, że funkcja zostanie wstawiona nawet z tym słowem kluczowym, chyba że użyje czegoś takiego jak __always_inline__atrybut gcc .
Brett Hale
11

Napisałem tę wersję, która działa dla MSVC, GCC, C i C ++.

#if defined(__cplusplus) && !defined(__GNUC__)
#   include <algorithm>
#   define MIN std::min
#   define MAX std::max
//#   define TMIN(T, a, b) std::min<T>(a, b)
//#   define TMAX(T, a, b) std::max<T>(a, b)
#else
#       define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
                ({ \
                        decltype(lexpr) lvar = (lexpr); \
                        decltype(rexpr) rvar = (rexpr); \
                        lvar binoper rvar ? lvar : rvar; \
                })
#       define _CHOOSE_VAR2(prefix, unique) prefix##unique
#       define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
#       define _CHOOSE(binoper, lexpr, rexpr) \
                _CHOOSE2( \
                        binoper, \
                        lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
                        rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
                )
#       define MIN(a, b) _CHOOSE(<, a, b)
#       define MAX(a, b) _CHOOSE(>, a, b)
#endif
Matt Joiner
źródło
Poparłem głosowanie, ale identyfikatory zaczynające się od znaku podkreślenia poprzedzonego wielką literą są zastrzeżone.
dreamlax
8

Jeśli potrzebujesz min / max, aby uniknąć kosztownej gałęzi, nie powinieneś używać operatora trójskładnikowego, ponieważ kompiluje się on do skoku. Poniższy link opisuje przydatną metodę implementacji funkcji min / max bez rozgałęziania.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

Cib
źródło
1
Jeśli kompilator jest wystarczająco inteligentny, może uniknąć gałęzi
Axel Gneiting
2
Jeśli optymalizacja jest włączona, wszystkie nowoczesne kompilatory będą w większości przypadków emitować ruch warunkowy zamiast gałęzi, więc nie ma sensu używać takich hacków.
Krzysztof Kosiński,
1
Absolutnie prawda, nie mam pojęcia, na co wtedy patrzyłem, minęło trochę czasu. Zarówno gcc, jak i clang unikają gałęzi z opcją -O, zarówno na x86, jak i armv7a.
cib
6

@David Titarenco przybity go tutaj , ale pozwól mi przynajmniej czyste go nieco, aby wyglądać dobrze i pokazać zarówno min() i max() razem zrobić kopiując i wklejając stąd łatwiejsze. :)

Aktualizacja 25 kwietnia 2020 r .: Dodałem również sekcję 3, aby pokazać, jak można by to zrobić z szablonami C ++, jako cenne porównanie dla osób uczących się zarówno C i C ++, lub przechodzących z jednego do drugiego. Zrobiłem co w mojej mocy, aby być dokładnym, rzeczowym i poprawnym, aby uczynić tę odpowiedź kanonicznym odniesieniem, do którego mogę powracać raz po raz, i mam nadzieję, że okaże się tak przydatna jak ja.

1. Stary sposób makro C:

Ta technika jest powszechnie stosowana, szanowana przez tych, którzy wiedzą, jak ją właściwie stosować, „de facto” sposób robienia rzeczy, i dobrze jest ją stosować, jeśli jest właściwie stosowana, ale buggy (pomyśl: efekt podwójnej oceny ), jeśli kiedykolwiek przekazuj wyrażenia, w tym przypisanie zmiennych, w celu porównania:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

2. Nowy i ulepszony sposób „ wyrażenia instrukcji gcc” :

Ta technika pozwala uniknąć powyższych skutków ubocznych i błędów „podwójnej oceny” i dlatego jest uważana za lepszy, bezpieczniejszy i „bardziej nowoczesny” sposób GCC C. Spodziewaj się, że będzie działać zarówno z kompilatorami gcc, jak i clang, ponieważ clang jest z założenia zgodny z gcc (patrz uwaga clang na dole tej odpowiedzi).

ALE: W dalszym ciągu uważaj na efekty „ cieniowania zmiennych ”, ponieważ wyrażenia instrukcji są najwyraźniej wbudowane i dlatego NIE mają własnego zasięgu zmiennej lokalnej!

#define max(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a > _b ? _a : _b;       \
})

#define min(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a < _b ? _a : _b;       \
})

Zauważ, że w wyrażeniach instrukcji gcc ostatnie wyrażenie w bloku kodu jest tym, co jest „zwracane” z wyrażenia, tak jakby zostało zwrócone z funkcji. Dokumentacja GCC mówi to w ten sposób:

Ostatnią rzeczą w instrukcji złożonej powinno być wyrażenie, po którym następuje średnik; wartość tego podwyrażenia służy jako wartość całego konstruktu. (Jeśli użyjesz innego rodzaju instrukcji na końcu nawiasów klamrowych, konstrukcja ma typ void, a zatem nie ma żadnej wartości).

3. Sposób szablonu C ++:

Uwaga C ++: jeśli używam C ++, szablony są prawdopodobnie zalecane zamiast tego typu konstrukcji, ale ja osobiście nie lubię szablonów i prawdopodobnie użyłbym jednej z powyższych konstrukcji w C ++, ponieważ często używam i preferuję style C również we wbudowanym C ++.

W tej sekcji dodano 25 kwietnia 2020 r .:

W ciągu ostatnich kilku miesięcy robiłem mnóstwo C ++, a presja, by preferować szablony nad makrami, tam gdzie jest to możliwe, w społeczności C ++ jest dość silna. W rezultacie coraz lepiej używam szablonów i chcę tutaj umieścić wersje szablonów C ++, aby uzyskać kompletność i uczynić to bardziej kanoniczną i dokładną odpowiedzią.

Oto, co podstawowe szablon funkcji wersje max()i min()może wyglądać jak w C ++:

template <typename T>
T max(T a, T b)
{
    return a > b ? a : b;
}

template <typename T>
T min(T a, T b)
{
    return a < b ? a : b;
}

Przeczytaj dodatkowe informacje na temat szablonów C ++ tutaj: Wikipedia: Szablon (C ++) .

Jednak zarówno max()i min()są już częścią biblioteki standardowej C ++, w <algorithm>nagłówku ( #include <algorithm>). W standardowej bibliotece C ++ są one zdefiniowane nieco inaczej niż mam je powyżej. Domyślne prototypy std::max<>()i std::min<>(), na przykład, w C ++ 14, patrząc na ich prototypy w linkach cplusplus.com powyżej, to:

template <class T> 
constexpr const T& max(const T& a, const T& b);

template <class T> 
constexpr const T& min(const T& a, const T& b);

Zauważ, że słowo kluczowe typenamejest aliasem class(więc ich użycie jest identyczne bez względu na to, czy mówisz <typename T>czy <class T>), ponieważ po wynalezieniu szablonów C ++ potwierdzono, że typ szablonu może być typem regularnym ( int,float , itd.), A nie tylko typ klasy.

Tutaj możesz zobaczyć, że oba typy wejściowe, jak również typ zwracany, są const T&, co oznacza „stałe odniesienie do typu T”. Oznacza to, że parametry wejściowe i wartość zwracana są przekazywane przez odniesienie zamiast przez wartość . To jest jak przekazywanie wskaźników i jest bardziej wydajne w przypadku dużych typów, takich jak obiekty klasy. constexprCzęść funkcji modyfikuje samą funkcję i wskazuje, że funkcja musi być w stanie być oceniany w czasie kompilacji (przynajmniej w przypadku określonych constexprparametrów wejściowych), jednak jeśli nie można ocenić w czasie kompilacji, to domyślnie powrotem do ocena w czasie wykonywania, jak każda inna normalna funkcja.

Aspekt czasu kompilacji constexprfunkcji C ++ sprawia, że ​​jest ona trochę podobna do makropolecenia C, ponieważ jeśli ocena constexprfunkcji kompilacji jest możliwa dla funkcji, zostanie ona wykonana w czasie kompilacji, podobnie jak podstawienie makra MIN()lub MAX()ewentualnie być w pełni oceniany podczas kompilacji również w C lub C ++. Aby uzyskać dodatkowe odniesienia do informacji o szablonie C ++, patrz poniżej.

Bibliografia:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. MIN i MAX w C
  4. Dodano dodatkowe odwołania do szablonów C ++, kwiecień 2020:
    1. ***** Wikipedia: Szablon (C ++) <- WIELKIE dodatkowe informacje na temat szablonów C ++!
    2. (Moje własne pytanie i odpowiedź): Dlaczego `constexpr` jest częścią prototypu szablonu C ++ 14 dla` std :: max () `?
    3. Różnica między „constexpr” i „const”

Notatka Clang z Wikipedii :

[Clang] ma działać jako zastępczy element zastępczy GNU Compiler Collection (GCC), obsługując większość jego flag kompilacyjnych i nieoficjalne rozszerzenia językowe.

Gabriel Staples
źródło
Do downvoter z ostatnich 24 godzin: dobra wiadomość! Usunąłem moją sekcję 4, którą dodałem wczoraj z sekcją 3 i zamiast tego umieściłem ją tutaj . Zachęcamy do ponownej oceny mojej odpowiedzi i wyrażenia jej opinii, jeśli proszę, ponieważ włożyłem w nią wiele dobrych informacji i dołożyłem wszelkich starań, aby była to solidna, pomocna, kanoniczna odpowiedź z korzyścią dla wszystkich. Teraz wrócił do skupienia. :) Dzięki!
Gabriel Staples
4

Warto zauważyć, że myślę, że jeśli zdefiniujesz mini maxz trzeciorzędowym, takim jak

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

następnie, aby uzyskać ten sam wynik dla specjalnego przypadku fmin(-0.0,0.0)i fmax(-0.0,0.0)musisz zamienić argumenty

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
Bozon Z.
źródło
Nadal nie będzie działać dla NaN. fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
greggo
@greggo, podałem lepszą odpowiedź tutaj stackoverflow.com/a/30915238/2542702
Z bozonem
4

Wygląda na to, że Windef.h(la #include <windows.h>) ma maxi min(małe litery) makra, które również cierpią z powodu trudności z „podwójną oceną”, ale są dostępne dla tych, którzy nie chcą ponownie tworzyć własnych :)

rogerdpack
źródło
12
Czy jesteś w ogóle zaskoczony?
Matt Joiner,
2

Wiem, że facet powiedział „C” ... Ale jeśli masz szansę, użyj szablonu C ++:

template<class T> T min(T a, T b) { return a < b ? a : b; }

Wpisz bezpieczny i bez problemów z ++ wspomnianym w innych komentarzach.

Bas Kuenen
źródło
16
Argumenty powinny być stałymi odwołaniami, nigdy nie wiadomo, co przejdzie użytkownik.
nmikhailov
6
Taka funkcja została już ustandaryzowana ( std :: min ).
dreamlax
C ++ ma wiele standardowych funkcji do większości normalnych celów, nie wymyślaj ponownie koła. Jednak stwardnienie rozsiane określa również własne min / max, które czasami powoduje problem
phuclv
0

Maksymalnie dwie liczby całkowite ai bto (int)(0.5((a+b)+abs(a-b))). Może to również działać z podwójnymi (double)i fabs(a-b)dla nich (podobnie dla pływaków)

NRZ
źródło
Przepraszam, jeśli to źle, jestem początkującym językiem C, ale ten kod działa dla mnie
NRZ
2
Nie jestem pewien, czy to działa z liczbami całkowitymi. Matematyka zmiennoprzecinkowa ma nieliniową precyzję.
Treesrule14
Aby rozwinąć komentarz @ Treesrule14: To nie działa, ponieważ komputery nie traktują liczb w taki sam sposób, jak matematyków. Liczba zmiennoprzecinkowa ma problemy z zaokrąglaniem, więc uzyskanie poprawnej odpowiedzi jest mało prawdopodobne. Nawet jeśli użyjesz matematycznych liczb całkowitych, MAX_INT + MAX_INT daje -2, więc maksimum (MAX_INT, MAX_INT) przy użyciu formuły wyszedłoby jako -1.
user9876
-3

Najprostszym sposobem jest zdefiniowanie go jako funkcji globalnej w .hpliku i wywołanie go w dowolnym momencie, jeśli twój program jest modułowy z dużą ilością plików. Jeśli nie, double MIN(a,b){return (a<b?a:b)}to najprostszy sposób.

srezat
źródło
1
@ Technosaurus Byłoby pomocne, gdybyś opisał, dlaczego to rozwiązanie jest złe, a nie tylko tak.
Tur1ng
@technosaurus, twoja odpowiedź jest naprawdę nieprzydatna. Po uruchomieniu wygląda na to, że funkcja jest całkowicie niepoprawna (brakujące typy parametrów wejściowych, brak średnika po instrukcji return), a konwersja danych wejściowych int na double jest złym sposobem na zrobienie czegoś, więc typ nie powinien być podwójny. Lepiej byłoby tutaj zdefiniowanie lub wyrażenie instrukcji (np. Patrz tutaj ), ale jeśli funkcja, rozważ utworzenie jednej funkcji do tego dla typów int32_t, jednej dla typów uint32_t i jednej dla typów zmiennoprzecinkowych lub podwójnych, w sumie 3 różne funkcje.
Gabriel Staples
1
@GabrielStaples Ta odpowiedź powinna być oflagowana jako brak odpowiedzi - nic na to nie poradzę. Chociaż może to być wykorzystane jako przykład tego, jak najbardziej się mylić w najmniejszej ilości miejsca. Polecenie globalnych funkcji w nagłówku (nawet statycznym niewzruszonym?) Złamie kod przy pomocy ponad 2 jednostek kompilacyjnych, nawet nie kompiluje, nazywając funkcję jak makro, domyślnie ints jak jej 1989, zwracając podwójną bez wyraźnego powodu, domyślnie obsady, które w najlepszym wypadku będą powodować ostrzeżenia ... a co najważniejsze, NIE ODPOWIADA NA PYTANIE - nie ogólne, nie bezpieczne dla typu i zdecydowanie nie najlepsze
technosaurus
Każdy z tych problemów zasługuje na dalszą krytykę, której nie można opisać wystarczająco szczegółowo.
technosaurus