1

I have a C++ program using SDL. During rendering, I need to draw some graphics components. I sometimes need to convert double variables, rounded to one decimal, to std::string.

For this, I'm currently using a ostringstream object and it works fine.

std::ostringstream ss;
ss << std::fixed << std::setprecision(1) << x;

However, I'm wondering if this way of converting variables is a good idea with regards to performance.

I've tried to round the double variables with std::to_string(std::round(x * 10) / 10)), but it didn't work -- I still got output like 2.500000000.

  • Is there another solution?
  • Does ostringstream incur a heavy cost?
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Grégoire Borel
  • 1,880
  • 3
  • 33
  • 55
  • 1
    considering performance: the `ostringstream` will use a `locale` which invokes some overhead, otherwise if you re-use a single `ostringstream` during your render call (single allocation / growing of the internal buffer) then there should be no more overhead than `sprintf` – BeyelerStudios Jan 12 '16 at 13:15
  • *but it didn't work.* you should elaborate on that. – NathanOliver Jan 12 '16 at 13:17
  • Just in case you really *mean* "it did not work", you *did* compile with a C++11 compiler, or your compiler set to (at least) C++11 standard, did you? – DevSolar Jan 12 '16 at 13:19
  • I did compile with a C++11 compiler. Didn't change anything. I still had my variable set to 2.500000000 (for example). – Grégoire Borel Jan 12 '16 at 13:24
  • 1
    @Q3SanD: Well, the `std::setprecision` turns this question on its head for sure. Deleted my answer as it turned out to be barking up the wrong tree. – DevSolar Jan 12 '16 at 13:39
  • Thanks for the edit. Does this mean I am left with this solution? Can't I use the solution I've tried with `std::round` and then use `std::string`? – Grégoire Borel Jan 12 '16 at 14:03

1 Answers1

3

You can't specify the precision with std::to_string as it is a direct equivalent to printf with the parameter %f (if using double).

If you are concerned about not allocating each time the stream, you can do the following :

#include <iostream>
#include <sstream>
#include <iomanip>

std::string convertToString(const double & x, const int & precision = 1)
{
    static std::ostringstream ss;
    ss.str(""); // don't forget to empty the stream
    ss << std::fixed << std::setprecision(precision) << x;

    return ss.str();
}


int main() {
    double x = 2.50000;

    std::cout << convertToString(x, 5) << std::endl;
    std::cout << convertToString(x, 1) << std::endl;
    std::cout << convertToString(x, 3) << std::endl;

    return 0;
}

It outputs (see on Coliru) :

2.50000

2.5

2.500

I didn't check the performance though... but I think you could even do better by encapsulating this into a class (like only call std::fixed and std::precision once).

Otherwise, you could still use sprintf with the parameters that suits you.


Going a little further, with an encapsulating class that you could use as a static instance or a member of another class... as you wish (View on Coliru).

#include <iostream>
#include <sstream>
#include <iomanip>

class DoubleToString
{
public:
    DoubleToString(const unsigned int & precision = 1)
    {
        _ss << std::fixed;
        _ss << std::setprecision(precision);
    }

    std::string operator() (const double & x)
    {
        _ss.str("");
        _ss << x;
        return _ss.str();
    }

private:
    std::ostringstream _ss;

};


int main() {
    double x = 2.50000;

    DoubleToString converter;

    std::cout << converter(x) << std::endl;

    return 0;
}

Another solution without using ostringstream (View on Coliru) :

#include <iostream>
#include <string>
#include <memory>

std::string my_to_string(const double & value) {
  const int length = std::snprintf(nullptr, 0, "%.1f", value);

  std::unique_ptr<char[]> buf(new char[length + 1]);
  std::snprintf(buf.get(), length + 1, "%.1f", value);

  return std::string(buf.get());
}

int main(int argc, char * argv[])
{

    std::cout << my_to_string(argc) << std::endl;
    std::cout << my_to_string(2.5156) << std::endl;

}
dkg
  • 1,775
  • 14
  • 34
  • It's still using `ostringstream` (no choice I guess) but it's much cleaner than my solution. I won't mark it as answer but I'll upvote it. Thanks. – Grégoire Borel Jan 15 '16 at 09:08
  • 1
    @Q3SanD I added another solution without `ostringstream`, but I'm not sure it is more efficient, you should run a bench. – dkg Jan 15 '16 at 09:50