Search code examples
g++stringstreamlexical-cast

stringstream: why does "showpoint" behave similar as "fixed"?


I'd like to write my own lexical_cast which preserves the decimal point when converting double to std::string. So I'm using ostringstream and set the flag std::ios::showpoint:

#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>

template <typename Source>
std::string my_string_cast(Source arg){
   std::ostringstream interpreter;
   interpreter.precision(std::numeric_limits<Source>::digits10);
   interpreter.setf(std::ios::showpoint);
   interpreter << arg;
   return interpreter.str();
}

int main(int argc, char** argv) {
   std::cout << my_string_cast(1.0) << std::endl;
   std::cout << my_string_cast(5.6) << std::endl;
   std::cout << my_string_cast(1.0/3.0) << std::endl;
   return 0;
}

This, however, prints unnecessary 0 digits, a behaviour I'd expect from setting std::ios::fixed but not std::ios::showpoint:

1.00000000000000
5.60000000000000
0.333333333333333

Without setting std::ios::showpoint it gives

1
5.6
0.333333333333333

but I want something like this:

1.0
5.6
0.333333333333333

Any easy way?


Solution

  • After a long time looking through the code of the std library it seems everything gets handed over to some printf type function: __builtin_vsnprintf(__out, __size, __fmt, __args). The format string __fmt is set depending on the flags set on the ostringstream object and can be queried using

    std::ostringstream s;
    // ...
    char format[50];
    std::__num_base::_S_format_float(s,format,'\0');
    

    The default format string is %.*g which is used as in printf("%.*g",precision,x); where precision is an int and x the double to be printed. For the other flags we get:

    s.setf(std::ios::fixed);      // %.*f
    s.setf(std::ios::showpoint);  // %#.*g
    

    Yet the format %#g doesn't just keep the decimal point but also keeps all trailing zeros. The doc says about the usage of # combined with g:

    "printf" will always print out a decimal point and trailing zeros will not
    be removed; usually 'g' and 'G' remove trailing zeros.
    

    Unfortunately, I can't find any other printf format string which behaves as nice as %g but always keeps the decimal point, so I guess something along the lines of dschaeffer's answer might well be the best.