Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locale-independent strtod implementation

Tags:

c

string

locale

I have a library which needs to parse double numbers which always use a point '.' as decimal separator. Unfortunately for this case, strtod() respects the locale which might use a different separator and thus parsing can fail. I can't setlocale() - it isn't thread-safe. So I'm searching for a clean locale-independent strtod implementation now. I have found multiple implementations so far, but all of them look hacky or just like bad code. Can someone recommend a well-tested, working, clean (ANSI) C implementation for me?

like image 535
bert Avatar asked Jan 03 '10 11:01

bert


4 Answers

Grab some known implementation (that doesn't depend on atof), such as the one distributed with ruby: ruby_1_8/missing/strtod.c.

like image 115
przemoc Avatar answered Nov 18 '22 04:11

przemoc


Warning: The proposed implementation from ruby contains bugs. I wouldn't mind the small difference pointed out by gavin, but if you try to parse something like "0.000000000000000000000000000000000000783475" you will get 0.0 instead of 7.834750e-37 (like the stock strtod() returns.)

Other solution:

#include <sstream>
#include "strtod_locale_independent.h"

extern "C" double strtod_locale_independent(const char* s)
{
     std::istringstream text( s );
     text.imbue(std::locale::classic());
     double result;
     text >> result;
     return result;
}

I don't know how fast this is, though.

like image 33
Florian Kusche Avatar answered Nov 18 '22 03:11

Florian Kusche


Following the answer above, I tried using the Ruby implementation at ruby_1_8/missing/strtod.c. However, for some inputs this gives different answers to gcc's built-in parser and to strtod from stdlib.h, both on Mac and on Linux platforms:

char * endptr ;
double value1 = 1.15507e-173 ;
double value2 = strtod( "1.15507e-173", &endptr ) ;
double value3 = test_strtod( "1.15507e-173", &endptr ) ;
assert( sizeof( double ) == sizeof( unsigned long ) ) ;
printf( "value1 = %lg, 0x%lx.\n", value1, *(unsigned long*)( &value1 ) ) ;
printf( "value2 = %lg, 0x%lx.\n", value2, *(unsigned long*)( &value2 ) ) ;
printf( "value3 = %lg, 0x%lx.\n", value2, *(unsigned long*)( &value3 ) ) ;
assert( value1 == value2 ) ;
assert( value1 == value3 ) ;

which prints

value1 = 1.15507e-173, 0x1c06dace8bda0ee0.
value2 = 1.15507e-173, 0x1c06dace8bda0ee0.
value3 = 1.15507e-173, 0x1c06dace8bda0edf.
Assertion failed: (value1 == value3), function main, file main.c, line 16.

So my advice is to test the chosen implementation before use.

like image 26
Gavin Band Avatar answered Nov 18 '22 05:11

Gavin Band


There's also gdtoa available on netlib, BSD style license: http://www.netlib.org/fp/gdtoa.tgz

like image 1
Spudd86 Avatar answered Nov 18 '22 03:11

Spudd86