Please subscribe to the official Codeforces channel in Telegram via the link https://t.me/codeforces_official. ×

### -is-this-fft-'s blog

By -is-this-fft-, history, 6 years ago,

I often, not that often, but still often see things like this while hacking:

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


While these are not as common as other (dubious) preprocessor macros, I still see these being used fairly commonly. There are, in my opinion, several downsides to using these -- if the inputs were functions, one of them gets executed twice.

So I want to ask, is there any advantage to using these over std::min(a, b) and others?

• +64

 » 6 years ago, # |   +18 Maybe some legacy from ancient times when those persons coded in C.
 » 6 years ago, # |   +13 Standard min function compares two variables of the same data type. It throws an error if you try to compare different data types (int with long long or float with double). The macro defined helps you avoid that!
•  » » 6 years ago, # ^ |   +35 Good point, but from a type-safety viewpoint I'm not sure that is a good idea. This certainly sounds like unexpected behaviour might happen in certain situations.
•  » » » 6 years ago, # ^ |   -17 Dude, it's competitive programming. Who cares about type-safety?
•  » » » » 6 years ago, # ^ |   +101 I care. It prevents bugs. If I take min from int and double then probably that was not what I was going to do. If it was then I can put there explicit cast which is no large cost and if it wasn't then I am richer by long time I would need to find that.
 » 6 years ago, # |   +11 I'm not really sure about this, but maybe an if is a bit faster than calling a function?
•  » » 6 years ago, # ^ |   +5 Apparently it is ...a bit. #define MIN(a,b) (a) < (b) ? (a) : (b) int main () { volatile int x; /* volatile to prevent the compiler */ volatile int a = 0; /* from optimising away too much */ for (volatile int i = 0; i < 1 << 30; i++) { x = MIN(i, a); } return x; } This code takes on average 2.31 s to run when compiled without -O2, and 2.20 s with, on my computer. Swapping the #define with #include  and the MIN with std::min gives on average 3.32 s without -O2 and 2.60 s with. And that was with using volatile to prevent optimizing.So if you optimize like Codeforces does, it's about a 400ms difference on over 109 iterations. It's entirely negligible if you do it a sane amount of times.
•  » » » 6 years ago, # ^ |   0 Actually, std::min and std::max are exactly the same as if on an optimizing compiler: https://godbolt.org/g/hYJ8dJIn your case I suggest removing volatile and looking at the produced assembly output instead. It should be the same too.
 » 6 years ago, # |   +18 Let's suppose you call something like a = MIN(a, foo(x, y, z)), where f(*) is a complicated and time-consuming function. The macro replaces that with "a < foo(x, y, z) ? a : foo(x, y, z)", which means you have the potential to call f(*) twice. And it gets worse if f(*) is dependent on global variables or changes them.Long story short: stick with the std::min() and std::max() functions, or use an if statement instead. There are virtually NO advantages to using those deprecated macros with today's optimizations of C++11 compilers.Take care! :)
•  » » 6 years ago, # ^ | ← Rev. 2 →   +14 Also writing your own code means more bugs. For example, consider int x = 5 + MIN(1, 2); with the above macros.
•  » » » 6 years ago, # ^ |   +10 MIN(x++, y++) may also lead to interesting results.
•  » » 6 years ago, # ^ |   +13 It's especially fun if f(*) reads from input!
 » 6 years ago, # |   +110 That's an awful thing.Consider the following segment tree implementation: int get(int l, int r, int L, int R, int v) { if (r < L || l > R) return inf; if (l <= L && R <= r) return T[v]; return MIN(get(l, r, L, (L + R) / 2, 2 * v), get(l, r, (L + R) / 2 + 1, R, 2 * v + 1)); } What do you think is the complexity of this implementation?
•  » » 6 years ago, # ^ |   +13 SpoilerIs it O(n)?
•  » » » 6 years ago, # ^ |   0 For sure.
 » 6 years ago, # |   +5 Maybe the people use this examples to teach how to create "functions" with defines, but they don't make clear that this functions are already created. Or at least that was what happened to me.