Question

Given the following code :

istringstream i("2.11099999999999999999");

double d;
if (!(i >> d)) {d = 0;}

cout << d << endl;

The output is 2.111 .

I want to have the ability of working with long numbers , float numbers (floating point included) , however when I convert my istringstream to double , I get a rounded number .

How can I prevent that ? How can I keep the given input as-is ?

Regards

Was it helpful?

Solution

In this case, you can't prevent it. double is not capable of precisely representing the value 2.11099999999999999999, and none of the values it can represent distinguishes 2.11099999999999999999 from 2.111.

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html should tell you what you need to know, and possibly more.

If you use a different example, where double can represent values that distinguish between the rounded and unrounded value, then you could do this:

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

int main() {
    std::istringstream iss("2.1109999");
    double d;
    iss >> d;
    std::cout << d << "\n";
    std::cout << std::setprecision(10) << d << "\n";
}

Output:

2.111
2.1109999

You should be aware, though, that the value stored in d is not exactly 2.1109999:

std::cout << std::setprecision(20) << d << "\n";

Output (on my machine, yours may differ because some runtime libraries don't print to 20 s.f at all):

2.1109998999999999292

That's because double stores values in binary, not decimal. So it can only represent terminating binary fractions. 2.1109999 is not a terminating binary fraction for basically the same reason that one third is not a terminating decimal fraction.

So, there are two ways to keep the given input as-is (i.e. to represent that number precisely):

  1. don't convert to double, leave it as a string.
  2. don't convert to double, instead find or write a library that represents decimal fractions and/or rational numbers. For example, the GMP library has mpq_t or there's Boost.Rational.

OTHER TIPS

In the space of mathematics there are an infinite number of numbers, and a double is finite, i.e. it fits into 64 bits, so only a subset of such values are represented exactly. And actually 2.111 is not exactly represented either, because although numbers are printed with decimal points, underneath they really use binary, which is a mantissa (52 bits of precision) and an exponent plus sign bits.

As a mantissa has around 52 bits of precision, 2 to the power of 52 is 4,503,599,627,370,496

As you can see, this number has 15 digits after the initial 4 thus you get 15 figures of decimal precision.

For most purposes, this precision is good enough. For those occasions where you need more precision, or special cases which require large numbers and small changes, you need to adopt specialist techniques. There are some libraries that will provide that for you.

as said you can't have that kind of precision. you can always increase the number of displayed digits as explained here: Increase precision

You'll need to store the number in something other than a double, which is a binary representation.

Have a look around for a library that stores numbers as a decimal representation, I know that boost has one, http://svn.boost.org/svn/boost/sandbox/big_number/libs/multiprecision/doc/html/index.html, but there are many others.

If this seems a bit of a heavyweight solution, try reading the istringstream a character at a time, if it is a digit, convert it from a char to an int, then store in an array.

Untested code

string s = "2.11099999999999999999";
char num[s.size()];
for (int i = 0; i < s.size(); ++i) {
  if (isdigit(s[i])
    num[i] = s[i] - '0';
  else
    num[i] = s[i];
}

There are two separate conversions going on here: there's the conversion from text to double (iss >> d), and there's the conversion of that double to text (std::cout << d). The first one stores the best approximation of the input text into your double variable. The second one uses a default precision; it rounds the value to fit that precision, and suppresses trailing zeros. That's why you see 2.111. If you want to see more digits, use setprecision to increase the number of digits in the output. As others have said, anything beyond about 15 digits (input or output) is nonsense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top