Saturday, December 25, 2010

On min and max

Today I'm gonna tell you about a trap I've been falling into once a two years or something, and I believe many other C/C++ programmers have.

This will be about XXX_MIN and XXX_MAX macros from <limits.h>. As it clearly stands from their names, these macros are a minimum and a maximum numerical values which can be stored in a variable of some numerical type. That is, INT_MAX is a maximum value representable by a variable of type of int, and is precisely equal to 2^31–1 on most 32-bit systems; and INT_MIN equals –(2^31–1), or –INT_MAX. For unsigned types, XXX_MAX equals to 2^bits_per_type–1 and XXX_MIN is expected to be zero.

This is all good, and using these macros is strictly advised for writing fine cross-platform code, but, believe me, you'll find yourself in trouble as soon as you're using it with floating point types, because FLT_MIN, DBL_MIN and LDBL_MIN really ruin your party. For some diabolic reason they don't stand for what their integral type counterparts do; in fact these are a representation of the least positive number that can be encoded with the type. That's right, FLT_MIN is actually 1/FLT_MAX, DBL_MIN is 1/DBL_MAX, and so on.

The things turn even worse when you're in the C++ boat and using std::numeric_limits<>. This generic behaves badly, too, and on my opinion compromises the very idea of being a template.

Have a look at the minimalistic proof code below.

#include <iostream>
#include <limits>

template <typename T>
std::string check()
{
    T def = T(),  // initialized with 0
      min = std::numeric_limits<T>::min(),
      max = std::numeric_limits<T>::max();
    return min < def && def < max ? "true" : "false";
}

int main()
{
    std::cout 
        << "int: " << check<int>() 
        << ", double: " << check<double>() 
        << std::endl;
    return 0;
}

It for sure outputs


int: true, double: false

What can I say? Thank you Kernighan, and thank you Ritchie! See you in hell guys, for the biggest evil on Earth being an inconsistent design.