How can I improve formatting number with commas performance?
Asked Answered
B

2

4

I'm using the following method to format a number with commas:

template<class T>
static std::string FormatNumberWithCommas(T value, int numberOfDecimalPlaces = 0)
{
    std::stringstream ss;
    ss.imbue(std::locale(""));
    ss.precision(numberOfDecimalPlaces);
    ss << std::fixed << value;
    return ss.str();
}

Profiling has show this method to take a significant amount of time relative to other code. Specifically the profiler has identified the line:

ss.imbue(std::locale(""));

And within that I believe it is the std::locale("") that is taking long. How can I improve the performance of this method? If it requires using something other than stringstream or doing something semi-hacky in this particular method I'm open to it.

Beals answered 7/10, 2011 at 23:25 Comment(2)
Cache the result? (Assuming you don't expect the locale to change while your program is running. (If that can even happen.))Rexferd
Hooray for profiling! :)Cleasta
G
4

You could start by making the string stream a static variable:

{
  static std::stringstream ss;
  static bool ss_init = false;
  static std::string emtpy_string;
  if (!ss_init) { ss.imbue(std::locale("")); ss_init = true; }

  ss.str(empty_string);
  // ...
}

If that's still a bottleneck, you could look at an alternative formatting library like fastformat.

Gillie answered 7/10, 2011 at 23:54 Comment(3)
ah nice.. since this method is in a class I was thinking of using a static class member, didn't even think of static local variable thanksBeals
In a class you can just make the stream an ordinary member variable (not static) and have the constructor set the locale. (If you made it static, you'd have to figure out a way to initialize it, probably something similar to what I did with the static bool.)Gillie
@okorz001 It is true for c++03, for c++11 the static locals are guaranteed by runtime in a thread-safe manner.Oskar
R
2
1 using namespace std;

2 template <typename T>
3 string AddCommas(T data);

4 template <>
5 string AddCommas<int>(int data)
6 { 
7   stringstream ss; ss << data; string s = ss.str();
8   if (s.length() > 3)
9       for (int i = s.length()-3; i > 0; i -= 3)
10          s.insert(i,",");
11  return s;
12 }

You can use the same code for long. Double is a little trickier.

13 template <>
14 string AddCommas<double>(double data)
15 {
16  stringstream ss; ss << fixed << data; string s = ss.str();
17  string intPart, decPart = "";
18  int pos = s.find('.');
19  if (pos != string::npos) {
20      intPart = s.substr(0,pos);
21      decPart = s.substr(pos,pos-s.length());
22      //remove trailing zeros
23      for (int i = decPart.length()-1; i > 0; i--) {
24          if (decPart[i] == '0') decPart.erase(i);
25          else break;
26      }
27      // remove decimal point if no decimals
28      if (decPart.length() == 1) decPart = "";
29  }
30  else intPart = s;
31  //insert commas
32  if (intPart.length() > 3) {
33      for (int i = intPart.length()-3; i > 0; i -= 3) intPart.insert(i,",");
34  }
35  s = intPart + decPart;
36  return s;
37 }

You can make one for float. The only problem arises if you want to set precision. Then you have to just make a stand alone function and add a parameter for precision. Also you would need to change some of the code:

ss << fixed << setprecision(precision) << data; //...

As well as remove lines 22 through 28.

Roland answered 10/12, 2012 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.