# Timo Heinäpurola

• Posts - 21

## When it makes sense to reinvent the wheel

A few years back I bumped into a rather peculiar optimization case. I did some performance analysis on mesh loading because at the time it took about 15 seconds to load a relatively small ANSI encoded mesh file. 15 seconds! I tracked the reason down to a call to atof, which is what I would use for parsing floating point values from strings.

Having found out what the reason was I decided to write my own implementation of floating point parsing that would concentrate on the essentials like speed instead of doing all kinds of culture checks and what not. Having done this I found out it was a very good idea indeed. Loading times dropped from 15 seconds to much under a second! If my memory doesn’t deceive me the speedup factor was somewhere around 100.

I recently tested whether the issue with atof was still there and it turns out it’s not that bad but it’s there all right. Testing with 1 000 000 iterations it took about 1.318 seconds from atof and 0.1016 seconds from my custom implementation.

Here’s the implementation of the function. It could still be optimized further, of course, and the precision is not as good as with atof, but it’s good enough.

```RRFloat_t ParseFloat( const RRChar_t *str )
{
RRUInt_t offset = 0;

while ( str[offset] != '\0' &&
str[offset] != '-' &&
( str[offset] < '0' ||
str[offset] > '9' ) ) {
offset++;
if ( str[offset] == '\0' ) {
return 0.0f;
}
}

RRFloat_t val = 0.0f;
RRBool_t neg = false;
if ( str[offset] == '-' )
{
offset++;
neg = true;
}
RRFloat_t curNom = 1.0f;
while( str[offset] != '.' )
{
val = val + (RRFloat_t)( str[offset] - '0' );
offset++;

switch( str[offset] )
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
break;
default:
if ( neg )
return -val;
return val;
}

if ( str[offset] != '.' )
val *= 10.0f;
}
offset++;
curNom = 0.1f;
while( str[offset] >= '0' && str[offset] <= '9' )
{
val = val + curNom * (RRFloat_t)( str[offset] - '0' );
curNom *= 0.1f;
offset++;
}

if ( neg )
return -val;
return val;
}
```

Simple enough. What’s the purpose of this blog post, you might think? When need be, don’t be afraid to get your hands dirty and just do it yourself!