atof – root of some evil

So when it  comes  to  string to number conversion the atof, atoi …  functions   from good old C standard lib get very handy. Even if  you a  c++   programmer it  is easy as 123 to write for example:

float f = atof(stringbuffer);

Localisation
There are more than one reasons to  do better than that ! But let´s talk about localisation. What! You are a Hardcore c programmer  doing fancy lowlevel stuff ? Never care  about languages ? Never interact with user ? But guys you could be nailed down anyway!

Use case
Imagine you develop a nice small library  doing the coolest bit banging algorithm  or controlling the next mission to mars. You need to read/write a few parameters from a ASCII Text file( e.g. XML or ini style ). So lets say the file is like this:

[magic numbers]
P1 = 1.23
P2 = 0.234

So ,you code straightforward or even just modify existing code:

#include <stdlib.h>
float getMagicNumber(char *filename )
{
 char *stringbuffer=NULL;
 stringbuffer = readFromFile("P2",filename);
 float f = atof(stringbuffer);
 return (f);

};

The others

So know your code runs well and you implement some test clients and are ready to ship the library to the customer.

A few month later a phone call comes in saying your algorithm heavily failed on a simple uses case.  Fortunatly, you did implement a file logging and then you see that paramete P1 was set to  f = 0.0  during getMagicNumber. What happens ?

After a couple days(if you are lucky) of investagtion and testing it is quite clear :

The customers application  uses the function call :

 char *currentCategory = setlocale (LC_ALL,"");

to adjust his needs on his current enviroment which was set to french, because know they selling their product in France.Unfortunately this changes  entire locale  of the current program(process) and  unfortunately in french the decimal separator is a comma, so the atof drops all after the point.

 Doing it quick

If you really, really want to stay with atof you have to check for the current local setting ( to be precise , the decimal separator) like:


std::string getCurrentSeparator()
{ 
 struct lconv * lc;
 lc=localeconv();
 return(std::string(lc.decimal_point));
};

and take this into account.

Leave a Reply