In one part of our application there is the requirement to format numbers according to the user's locale.
The interface essentially looks like this:
std::string format_number(double number);
The old implementation looked like this:
std::string format_number(double number) {
using namespace std;
static const locale user_loc("");
ostringstream fmtstream;
fmtstream.imbue(user_loc);
fmtstream << std::fixed;
fmtstream << number;
return fmtstream.str();
}
We have now noticed that with our compiler (MSVC 2005), we can get a approx 10-20% speed up of this function (measured in isolation) by using num_put
directly:
std::string format_number(double number) {
using namespace std;
static const locale user_loc("");
ostringstream fmtstream;
fmtstream.imbue(user_loc);
fmtstream << std::fixed;
typedef char* CharBufOutIt;
typedef num_put<char, CharBufOutIt> np_t;
np_t const& npf = use_facet<np_t>(user_loc);
char buf[127];
const CharBufOutIt begin = &buf[0];
const CharBufOutIt end = npf.put(/*out=*/begin, /*format=*/fmtstream, /*fill=*/' ', number);
return std::string(begin, end);
}
This still seems suboptimal:
put
will call width(0)
on the passed format stream.Is the another "trick" to speed this up, or is this the most we can hope to get out of iostream?
And note that we cannot use sprintf here, as we need to make sure the full locale, including thousands separator, is used, and printf doesn't output the thousand separator.
Thanks to user 0x499602D2's answer I have been able to speed this up some more (even with a locale facet).
The point is to not use a stream for the formatting flags, since the /*format=*/
parameter to put
is only accessed for it's flags and locale, so a more basic object suffices:
std::string version_3(double number) {
using namespace std;
static const locale user_loc("");
ios fmtios(NULL); // instead of a full stream
fmtios.imbue(user_loc);
fmtios.setf(ios_base::fixed);
typedef char* CharBufOutIt;
typedef num_put<char, CharBufOutIt> np_t;
np_t const& npf = use_facet<np_t>(user_loc);
char buf[127];
const CharBufOutIt begin = &buf[0];
const CharBufOutIt end = npf.put(/*out=*/begin, /*format=*/fmtios, /*fill=*/' ', value);
return std::string(begin, end);
}
I prematurely had dismissed this version because I was not aware that you can actually legally pass a nullptr as stream buffer to basic_ios
. The docs for init show you can, although it will result in badbit
being set, but as far as I'm able to tell put
doesn't care about the state bits.