12

What is the most optimal way to achieve the same as this?

void foo(double floatValue, char* stringResult)
{
    sprintf(stringResult, "%f", floatValue);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Dolson
  • 300
  • 2
  • 8
  • 9
    You have to be wary with the API you have specified, as you are writing to a buffer you do not know the size of, so you cannot guarantee the safety of the above code. – fbrereto Aug 21 '09 at 20:16
  • 6
    C and C++ are different in this regard, so your question should be for one or the other... – Emile Vrijdags Aug 21 '09 at 20:30
  • 8
    Define optimal. Do you want fastest execution time, lowest memory usage, safest operation, most readable code, or even something else? Until this is clear, you're going to get a very wide range of answers. – TheUndeadFish Aug 21 '09 at 22:42
  • I second TheUndeadFish's comment. Are you asking for the fastest execution time? – Peter Mortensen Aug 22 '09 at 00:32
  • By "optimal" I mean fastest execution time. Assume that the stringResult is large enough to hold the result, say 100 bytes. (I've simplified the code snippet for the question) – David Dolson Aug 24 '09 at 18:48
  • 11
    How about some of the examples from the following: http://www.codeproject.com/KB/recipes/Tokenizer.aspx They are very efficient and somewhat elegant. –  Nov 04 '10 at 01:58

12 Answers12

23

I'm sure someone will say boost::lexical_cast, so go for that if you're using boost, but it's basically the same as this anyway:

 #include <sstream>
 #include <string>

 std::string doubleToString(double d)
 {
    std::ostringstream ss;
    ss << d;
    return ss.str();
 }

Note that you could easily make this into a template that works on anything that can be stream-inserted (not just doubles).

Tyler McHenry
  • 74,820
  • 18
  • 121
  • 166
  • 6
    I should note that, due to the way the question was posed (as changing from C to C++), I interpreted "most optimal" as "most idiomatic" rather than "fastest executing". – Tyler McHenry Aug 21 '09 at 20:47
  • Regarding execution time of this approach, see: http://stackoverflow.com/questions/1250795/very-poor-boostlexicalcast-performance – Reunanen Aug 22 '09 at 07:09
  • 1
    Even better: Use a template to handle any number of types, so you don't have to write an implementation for each conversion. – Matthew Iselin Aug 23 '09 at 02:37
  • 1
    This is the opposite of "optimal" in terms of execution time. It allocates memory for starters. – David Dolson Aug 24 '09 at 18:49
10

http://www.cplusplus.com/reference/iostream/stringstream/

double d=123.456;
stringstream s;
s << d; // insert d into s
phuclv
  • 37,963
  • 15
  • 156
  • 475
alanc10n
  • 4,897
  • 7
  • 36
  • 41
  • 1
    If you are going to use this method (personally I'd choose boost::lexical_cast...) then I'd suggest using an istringstream. It may be more lightweight than a stringstream and, more importantly, you cannot get "<<" the wrong way around. – MattyT Aug 23 '09 at 13:20
4

Boost::lexical_cast<>

greyfade
  • 24,948
  • 7
  • 64
  • 80
4

On dinkumware STL, the stringstream is filled out by the C library snprintf.

Thus using snprintf formatting directly will be comparable with the STL formatting part. But someone once told me that the whole is greater than or equal to the sum of its known parts.

As it will be platform dependent as to whether stringstream will do an allocation (and I am quite sure that DINKUMWARE DOES NOT YET include a small buffer in stringstream for conversions of single items like yours) it is truely doubtful that ANYTHING that requires an allocation (ESPECIALLY if MULTITHREADED) can compete with snprintf.

In fact (formatting+allocation) has a chance of being really terrible as an allocation and a release might well require 2 full read-modify-write cycles in a multithreaded environment unless the allocation implementation has a thread local small heap.

That being said, if I was truely concerned about performance, I would take the advice from some of the other comments above, change the interface to include a size and use snprintf - i.e.

bool 
foo(const double d, char* const p, const size_t n){
     use snprintf......
     determine if it fit, etc etc etc.
}

If you want a std::string you are still better off using the above and instantiating the string from the resultant char* as there will be 2 allocations + 2 releases involved with the std::stringstream, std::string solution.

BTW I cannot tell if the "string" in the question is std::string or just generic ascii chars usage of "string"

pgast
  • 1,657
  • 10
  • 11
  • BTW snprintf is terrible for uint32's approximately 10 time what a hand coded uitoa would be. I have done much work where we were publishing ascii numeric data (constrained by a protocol but since made binary) at rates approaching 1mm mps. NOT using snprintf was a huge win - think about it - branch prediction, the extra parse of the format string and having to handle all the different possible formats - ouch! – pgast Sep 07 '09 at 23:57
3

The best thing to do would be to build a simple templatized function to convert any streamable type into a string. Here's the way I do it:

#include <sstream>
#include <string>

template <typename T>
const std::string to_string(const T& data)
{
   std::ostringstream conv;
   conv << data;
   return conv.str();
}

If you want a const char* representation, simply substitute conv.str().c_str() in the above.

hb2pencil
  • 892
  • 1
  • 8
  • 13
  • 1
    Only do this if you don't have access to boost. And if you don't have access to boost begin trying to get it! :) – MattyT Aug 23 '09 at 13:22
  • 1
    You have basically written boost::lexical_cast! :) Clean, elegant and incredibly slow (because IOstreams are slow). – j_random_hacker Aug 25 '09 at 10:59
  • Don't replace `conv.str()` with `conv.str().c_str()`. Then you will return a pointer to a local variable which is immediately out of scope! – Tim Kuipers Sep 27 '16 at 12:43
2

I'd say sprintf is pretty much the optimal way. You may prefer snprintf over it, but it doesn't have much to do with performance.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • Since the printf family of functions has to parse the formatting string at run time, there ought to be a faster way that doesn't have to do that. – David Dolson Aug 24 '09 at 18:58
  • That's right, but since underlying part of the printf has no publicly available entry point, the only way to do it is either to go for writing that yourself (that would duplicate code and therefore increase loading time! ;-)) or neglect the performance penalty of parsing formatting string. You can't be serious about the performance impact of parsing 2 (two!) characters (well, 3 including NUL), can you? – Michael Krelin - hacker Aug 24 '09 at 20:10
2

I'd probably go with what you suggested in your question, since there's no built-in ftoa() function and sprintf gives you control over the format. A google search for "ftoa asm" yields some possibly useful results, but I'm not sure you want to go that far.

David
  • 2,164
  • 13
  • 11
2

Herb Sutter has done an extensive study on the alternatives for converting an int to a string, but I would think his arguments hold for a double as well.

He looks at the balances between safety, efficiency, code clarity and usability in templates.

Read it here: http://www.gotw.ca/publications/mill19.htm

1

_gcvt or _gcvt_s.

MSN
  • 53,214
  • 7
  • 75
  • 105
1

If you use the Qt4 frame work you could go :

double d = 5.5;
QString num = QString::number(d);
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
0

This is very useful thread. I use sprintf_s for it but I started to doubt if it is really faster than other ways. I came across following document on Boost website which shows performance comparison between Printf/scanf, StringStream and Boost.

Double to String is most common conversion we do in our code, so i'll stick with what i've been using. But, using Boost in other scenarios could be your deciding factor.

http://www.boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/performance.html

Patel
  • 399
  • 5
  • 12
0

In the future, you can use std::to_chars to write code like https://godbolt.org/z/cEO4Sd . Unfortunately, only VS2017 and VS2019 support part of this functionality...

#include <iostream>
#include <charconv>
#include <system_error>
#include <string_view>
#include <array>

int main()
{
    std::array<char, 10> chars;
    auto [parsed, error] = std::to_chars(
        chars.data(), 
        chars.data() + chars.size(), 
        static_cast<double>(12345.234)
    );
    std::cout << std::string_view(chars.data(), parsed - chars.data());
}

For a lengthy discussion on MSVC details, see https://www.reddit.com/r/cpp/comments/a2mpaj/how_to_use_the_newest_c_string_conversion/eazo82q/

Porsche9II
  • 629
  • 5
  • 17