19
double val = 0.1;
std::stringstream ss;
ss << val;
std::string strVal= ss.str();

In the Visual Studio debugger, val has the value 0.10000000000000001 (because 0.1 can't be represented). When val is converted using stringstream, strVal is equal to "0.1". However, when using boost::lexical_cast, the resulting strVal is "0.10000000000000001".

Another example is the following:

double val = 12.12305000012;

Under visual studio val appears as 12.123050000119999, and using stringstream and default precision (6) it becomes 12.1231. I don't really understand why it is not 12.12305(...).

Is there a default precision, or does stringstream have a particular algorithm to convert a double value which can't be exactly represented?

Thanks.

Benji XVI
  • 2,192
  • 1
  • 25
  • 24
Guillaume Paris
  • 10,303
  • 14
  • 70
  • 145

4 Answers4

20

You can change the floating-point precision of a stringstream as follows:

double num = 2.25149;
std::stringstream ss(stringstream::in | stringstream::out);
ss << std::setprecision(5) << num << endl;
ss << std::setprecision(4) << num << endl;

Output:

2.2515
2.251

Note how the numbers are also rounded when appropriate.

Community
  • 1
  • 1
nickolayratchev
  • 1,136
  • 1
  • 6
  • 15
  • 2
    in testing this in Visual Studio C++, I can only get this to work by calling `ss.setf(ios::fixed);` first. If I don't do that, then the precision does not change. – Sam Goldberg Oct 08 '13 at 14:48
  • 16
    use `std::setprecision(std::numeric_limits::digits10)` to get full double precision. – kuga Jul 17 '15 at 08:23
  • `digits10` does not work, at least on Visual Studio 2015. Try `18014398509481980.0` and `18014398509481982.0`. 17 is the minimum for full precision. See https://stackoverflow.com/q/6118231/280534 and try it at https://onlinegdb.com/Sk6yXmeY7 – Kevin Smyth Sep 19 '18 at 19:22
  • 1
    **cppreference** points out [here](https://en.cppreference.com/w/cpp/io/manip/setprecision) the max precision is attained with: `std::setprecision(std::numeric_limits::digits10 + 1)` – Max Levy Mar 30 '20 at 15:25
  • You can also use [`std::numeric_limits::max_digits10`](https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10) since C++11, see https://stackoverflow.com/a/22458961 for the difference between `std::numeric_limits::max_digits10` and `std::numeric_limits::digits10`. – Catree Aug 31 '22 at 13:15
18

For anyone who gets "error: ‘setprecision’ is not a member of ‘std’" you must #include <iomanip> else setprecision(17) will not work!

YuZ
  • 445
  • 5
  • 18
5

There are two issues you have to consider. The first is the precision parameter, which defaults to 6 (but which you can set to whatever you like). The second is what this parameter means, and that depends on the format option you are using: if you are using fixed or scientific format, then it means the number of digits after the decimal (which in turn has a different effect on what is usually meant by precision in the two formats); if you are using the default precision, however (ss.setf( std::ios_base::fmtflags(), std::ios_base::formatfield ), it means the number of digits in the output, regardless of whether the output was actually formatted using scientific or fixed notation. This explains why your display is 12.1231, for example; you're using both the default precision and the default formattting.

You might want to try the following with different values (and maybe different precisions):

std::cout.setf( std::ios_base::fmtflags(), std::ios_base::floatfield );
std::cout << "default:    " << value[i] << std::endl;
std::cout.setf( std::ios_base::fixed, std::ios_base::floatfield );
std::cout << "fixed:      " << value[i] << std::endl;
std::cout.setf( std::ios_base::scientific, std::ios_base::floatfield );
std::cout << "scientific: " << value[i] << std::endl;

Seeing the actual output will probably be clearer than any detailed description:

default:    0.1
fixed:      0.100000
scientific: 1.000000e-01
nmr
  • 16,625
  • 10
  • 53
  • 67
James Kanze
  • 150,581
  • 18
  • 184
  • 329
4

The problem occurs at the stream insertion ss << 0.1; rather than at the conversion to string. If you want non-default precision you need to specify this prior to inserting the double:

ss << std::setprecision(17) << val;

On my computer, if I just use setprecision(16) I still get "0.1" rather than "0.10000000000000001". I need a (slightly bogus) precision of 17 to see that final 1.

Addendum
A better demonstration arises with a value of 1.0/3.0. With the default precision you get a string representation of "0.333333". This is not the string equivalent of a double precision 1/3. Using setprecision(16) makes the string "0.3333333333333333"; a precision of 17 yields "0.33333333333333331".

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • I have tried with the following real 12.12305000012, in computer under visual studio it appears has 12.123050000119999 and using stringstream and default precision(6 isn't it?) it becomes 12.1231. I don't really understand why it it not 12.12305(..something) – Guillaume Paris Oct 15 '12 at 11:58
  • 1
    To six decimal places, 12.12305000012 *is* 12.1231. This rounding takes place at the insertion into the stream. You can control this rounding behavior via `setprecision` -- or you can simply use `Boost::lexical_cast`. `Boost::lexical_cast` also uses string streams, but it specifies the precision on insertion to try to ensure that nothing is lost. For floating point types, `lexical_cast` uses a bit more than the maximum precision so as to minimize losses. – David Hammen Oct 15 '12 at 12:06
  • ok because precision of 6 mean the total number of digit used, not the number of decimal – Guillaume Paris Oct 15 '12 at 12:19
  • 2
    Yep. What `setprecision` does depends on the setting of the stream's `floatfield`. This can be `fixed`, `scientific`, or (none). `setprecision(

    )` behaves similarly to the `"%.

    f"` format specifier in the C `printf` family of functions for `fixed`, `"%.

    e" for `scientific`, and `"%.

    g"` for (none).

    – David Hammen Oct 15 '12 at 13:00