Wednesday, September 19, 2007

To my noname friend: atoi and atof as they should never be done

Sometimes, when I get bored, I spend an hour or so reinventing the wheel. Earlier tonight I was asked how code like atoi worked. A quick look at an ASCII table and a few multiply-accumulates later, we have something like:
/* atoi - christopher.watford@gmail.com */
/* PUBLIC DOMAIN */
long atoi(const char *value) {
  long ival = 0, c, n = 1, i = 0, oval;
  for( ; c = value[i]; ++i) /* chomp leading spaces */
    if(!isspace(c)) break;
  if(c=='-' || c=='+') { /* chomp sign */
    n = (c!='-' ? n : -1); i++;
  }
  while(c = value[i++]) { /* parse number */
    if(!isdigit(c)) return 0;
    oval = ival; /* save ival for overflow detection */
    ival = (ival * 10) + (c - '0'); /* mult/accum */
    if(ival < oval) { /* report overflow/underflow */
      errno = ERANGE;
      return (n>0 ? LONG_MAX : LONG_MIN);
    }
  }
  return (n>0 ? ival : -ival);
}
Yes, this is quite ugly, and if I ever catch you writing something this cryptic I'll force a 15 page code review on you. However, one can tell how much fun atoi actually is once you take into account error checking!

Naturally our conversation drifted to atof--well in his defense I drifted to atof--and I decided I should write a compliant implementation. As it turns out implementing overflow and underflow checking for floating point numbers is much harder (and trickier too!) than for integers. Below are the pretty printed sources to both my atoi and atof implementations, along with links to download. They are in the public domain and you should use them at your own risk, because if I were to catch you using either of these I will hold a code review so harsh it would make a death row inmate cry.
View atoi source (download)
View atof source (download)