C++ 28 May 2011 19:04:13

C++ Convert String to Double Speed

(There is also a string-to-int performance test.)

A performance benchmark of which method is faster of converting an std::string to a double. The goal is ending up with a double of the value represented in an std::string.

The tested methods are:

Source for the test is at speed-string-to-double.cpp with cycle.h.

The compilers are Microsoft Visual C++ 2010 with _SECURE_SCL disabled, GNU g++ 4.6.0, and LLVM clang++ from Arch.


Tests were run for converting 100000 string containing doubles in the range +/- 99999.99999. The result for the naive loop and atof() are set as the baseline 100% and the other numbers is time spent relative to those. The naive loop wins by a large margin, but Boost.Spirit is the fastest correct implementation.

Windows: MSVC++ 2010

  • Compiler: MSVC++ 2010 _SECURE_SCL=0
  • Arch: Windows 7 64 bit, 1.60GHz Core i7 Q720, 8 GiB RAM
VC++ 2010TicksRelative to naiveRelative to atof()
naive43662201.000.05
atof()8273277418.951.00
strtod()8318919819.051.01
sscanf()16856838738.612.04
spirit qi189329174.340.23
lexical_cast33237440776.124.02
stringstream36194381682.904.37
stringstream reused24084839255.162.91

Linux: GNU g++ 4.6.0

  • Compiler: GNU g++ 4.6.0 -O3
  • Arch: VirtualBox on the Windows machine, VT-x, Arch Linux, kernel 2.6.38-ARCH, 1 GiB RAM
g++ 4.6.0TicksRelative to naiveRelative to atof()
naive46561591.000.15
atof()306054906.571.00
strtod()309639266.651.01
sscanf()5623519712.081.84
spirit qi207310624.450.68
lexical_cast13952140629.964.56
stringstream18472329839.676.04
stringstream reused10090540721.673.30

Linux: LLVM clang++ 2.9

  • Compiler: clang++ 2.9 -O3
  • Arch: VirtualBox on the Windows machine, VT-x, Arch Linux, kernel 2.6.38-ARCH, 1 GiB RAM
clang++ 2.9TicksRelative to naiveRelative to atof()
naive68048811.000.22
atof()308298654.531.00
strtod()308715144.541.00
sscanf()579039938.511.88
spirit qi244110413.590.79
lexical_cast14933983321.954.84
stringstream19123906628.106.20
stringstream reused10046140514.763.26

17 Responses to “C++ Convert String to Double Speed”

  1. on 29 May 2011 at 06:04:09 1.metagoto said …

    On my machine, Boost Spirit 2.4 with double_[ref(x) = _1] is 2 times slower than the naive loop. It competes with atof() and strtod()

  2. on 29 May 2011 at 10:37:11 2.Tino Didriksen said …

    I have added Boost.Spirit, but not sure if I used the correct parser; haven’t used it before. Correct me if there is an even faster way.

    Still, it’s very fast…

  3. on 29 May 2011 at 17:25:43 3.metagoto said …

    @Tino Didriksen
    Not sure if there is a faster way, but using Phoenix placeholders is Spirit’s spirit ;). We both have the exact same code.

  4. on 29 May 2011 at 21:05:36 4.Christian said …

    Where you using /Ox for VC++ ?

  5. on 29 May 2011 at 21:23:53 5.Tino Didriksen said …

    I was using /O2 /Ot, but changed to /Ox /Ot and updated the table. It’s a bit faster. Relatively, it’s the same though…these benchmarks aren’t about the absolutely fastest compiler or settings, but more about the overall faster method.

    Full cmdline copied from the IDE is: /I”C:\Applications\Boost\boost_1_46_1″ /Zi /nologo /W4 /WX- /Ox /Ot /Oy- /GL /D “_UNICODE” /D “UNICODE” /Gm- /EHsc /MT /GS- /Gy /arch:SSE2 /fp:precise /Zc:wchar_t /Zc:forScope /openmp /Fp”Release\TestProject01.pch” /FAcs /Fa”Release\” /Fo”Release\” /Fd”Release\vc100.pdb” /Gd /analyze- /errorReport:queue … let me know if you can see any flaws.

  6. on 30 May 2011 at 11:01:22 6.anon said …

    That naive algorithm sounds great, where can i buy it?
    You should paste it here too..

  7. on 30 May 2011 at 11:11:16 7.Tino Didriksen said …

    The full source code is available and linked in the post as http://tinodidriksen.com/uploads/code/cpp/speed-string-to-double.cpp

    Mind you, I say it’s naive for a reason. I am sure it doesn’t conform to quality expectations that the other algorithms do. It’s just to have a neutral point of comparison.

  8. on 30 May 2011 at 16:40:34 8.Hartmut Kaiser said …

    Two minor comments:

    a) You don’t need to utilize semantic actions with Spirit (and it is not true to say these are the spirit of Spirit). You can directly pass the variable to be filled:

    parse(nums[i].begin(), nums[i].end(), double_, x);

    This saves (at least) one double assignment.

    b) The current code lets Spirit parse from the string iterators, while most of the other codes parse from the plain character buffer. If you do the same for Spirit you’ll see further speedup:

    char const* str = nums[i].c_str();
    parse(str, &str[nums[i].size()], double_, x);

    Thanks for doing this analysis!
    Regards Hartmut

  9. on 30 May 2011 at 17:26:40 9.Tino Didriksen said …

    Alrighty, updated to use the latter way.

    Compared to the old code, that made the Spirit method 3% faster for VC++, 5% faster for g++, and no change for clang++.

  10. on 02 Aug 2011 at 12:11:49 10.Piotr said …

    Try this one (not mine, but works):
    http://www.leapsecond.com/tools/fast_atof.c

    It’s a replacement for atof() – much faster.

  11. on 20 Feb 2013 at 00:35:07 11.Arash Partow said …

    Have you considered including StrTk in your listings?

    The tests used in that library for string to double/float conversion are a little more extensive.

    At the end of the article a number of comparisons between, stdlib, Boost, Spirit and StrTk can be found using various hardware configurations/compilers etc.

    http://www.codeproject.com/KB/recipes/Tokenizer.aspx

  12. on 28 Apr 2014 at 16:15:45 12.Duke said …

    Nice list.
    @stringstream: when you only measure the timings of serializing the values (and not the storing the values in the stringstream container) the timings are identical with atof and strtod.

    e.g.
    {
    double tsum = 0.0;
    std::vector timings;
    timings.reserve(R);
    std::stringstream* ss = new std::stringstream[N];

    for (size_t r=0 ; r<R ; ++r) {
    for (size_t i=0 ; i<nums.size() ; ++i) {ss[i].str(nums[i]); }
    ticks start = getticks();
    for (size_t i=0 ; i> x;
    tsum += x;
    }
    ticks end = getticks();
    double timed = elapsed(end, start);
    timings.push_back(timed);
    }

    delete[] ss;
    std::cout << "stringstream without init: ";
    PrintStats(timings);
    std::cout << std::endl;
    std::cout << tsum << std::endl;
    }

  13. on 20 Jul 2014 at 15:45:42 13.Gabriel said …

    Here is the fastest generalized Method (float,double) with valid conversion tests!
    It is as fast as the fastest of your code! Testet with gcc 4.7 linux:

    #define white_space(c) ((c) == ‘ ‘ || (c) == ‘\t’)
    #define valid_digit(c) ((c) >= ‘0’ && (c) <= '9')

    template
    bool naive(T & r, const char *p) {

    // Skip leading white space, if any.
    while (white_space(*p) ) {
    p += 1;
    }

    r = 0.0;
    int c = 0; // counter to check how many numbers we got!

    // Get the sign!
    bool neg = false;
    if (*p == ‘-‘) {
    neg = true;
    ++p;
    }else if(*p == ‘+’){
    neg = false;
    ++p;
    }

    // Get the digits before decimal point
    while (valid_digit(*p)) {
    r = (r*10.0) + (*p – ‘0’);
    ++p; ++c;
    }

    // Get the digits after decimal point
    if (*p == ‘.’) {
    T f = 0.0;
    T scale = 1.0;
    ++p;
    while (*p >= ‘0’ && *p std::numeric_limits::max_exponent10 ){
    e = std::numeric_limits::max_exponent10;
    }else if(e < std::numeric_limits::min_exponent10 ){
    e = std::numeric_limits::max_exponent10;
    }
    // SECOND CHECK:
    if(c==0){return false;} // we got no exponent! this was not intended!!

    T scaleE = 1.0;
    // Calculate scaling factor.

    while (e >= 50) { scaleE *= 1E50; e -= 50; }
    //while (e >= 8) { scaleE *= 1E8; e -= 8; }
    while (e > 0) { scaleE *= 10.0; e -= 1; }

    if (negE){
    r /= scaleE;
    }else{
    r *= scaleE;
    }
    }

    // POST CHECK:
    // skip post whitespaces
    while( white_space(*p) ){
    ++p;
    }
    if(*p != ”){return false;} // if next character is not the terminating character

    // Apply sign to number
    if(neg){ r = -r;}

    return true;
    }

  14. on 20 Jul 2014 at 15:47:04 14.Gabriel said …

    here the link to the code:
    http://pastebin.com/dHP1pgQ4

  15. on 31 Mar 2015 at 12:06:15 15.me said …

    Your naive doesn’t read the exponent, as in “0.1E+01”.

  16. on 31 Mar 2015 at 14:16:06 16.Tino Didriksen said …

    Regarding exponents: Hence why I wrote “Boost.Spirit is the fastest correct implementation”, and I previously commented it wasn’t meant to be a conforming comparison function.

  17. on 17 Jul 2018 at 12:48:25 17.Jon said …

    Did anyone test the speed of the parsing routines of https://github.com/google/double-conversion/ (most famous for its printing routines using grisu3 from Florian Loitsch, 2010) ?

Subscribe to the comments through RSS Feed

Leave a Reply

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Anti-spam image