Учитывая следующий код:
istringstream i("2.11099999999999999999");
double d;
if (!(i >> d)) {d = 0;}
cout << d << endl;
Выход 2.111
,
Я хочу иметь возможность работать с длинными числами, числами с плавающей запятой (включая числа с плавающей запятой), однако при конвертации istringstream
чтобы удвоить, я получаю округленное число.
Как я могу предотвратить это? Как я могу сохранить данный ввод как есть?
С уважением
В этом случае вы не можете предотвратить это. double
не в состоянии точно представить значение 2.11099999999999999999, и ни одно из значений это Можно представляют отличает 2.11099999999999999999 от 2.111.
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html должен сказать вам, что вам нужно знать, и, возможно, больше.
Если вы используете другой пример, где double
может представлять значения, которые различают округленное и необоснованное значение, тогда вы можете сделать это:
#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";
}
Выход:
2.111
2.1109999
Вы должны знать, однако, что значение хранится в d
не совсем 2.1109999
:
std::cout << std::setprecision(20) << d << "\n";
Вывод (на моем компьютере ваш может отличаться, потому что некоторые библиотеки времени выполнения не печатают до 20 с.ф вообще):
2.1109998999999999292
Это потому что double
сохраняет значения в двоичный, не десятичный. Таким образом, он может представлять только конечные двоичные дроби. 2.1109999 не является конечной двоичной дробью по той же причине, что одна треть не является конечной десятичной дробью.
Таким образом, существует два способа сохранить данный ввод как есть (то есть точно представить это число):
double
оставь это как строку.double
вместо этого найдите или напишите библиотеку, которая представляет десятичные дроби и / или рациональные числа. Например, библиотека GMP имеет mpq_t
или есть Boost.Rational.В пространстве математики существует бесконечное число чисел и double
конечно, то есть он вписывается в 64 бита, поэтому только подмножество таких значений представляется точно. И фактически 2.111 также точно не представлен, потому что, хотя числа печатаются с десятичными точками, под ними действительно используется двоичный код, который представляет собой мантиссу (52 бита точности) и экспоненту со знаком плюс.
Так как мантисса имеет точность около 52 битов, 2 к степени 52 составляет 4,503,599,627,370,496
Как видите, это число имеет 15 цифр после начальных 4, таким образом, вы получите 15 цифр с десятичной точностью.
Для большинства целей эта точность достаточно хороша. В тех случаях, когда вам нужна большая точность, или в особых случаях, которые требуют большого количества и небольших изменений, вам необходимо принять специальные методы. Есть несколько библиотек, которые обеспечат это для вас.
как сказано, вы не можете иметь такую точность.
Вы всегда можете увеличить количество отображаемых цифр, как описано здесь: Повысить точность
Вам нужно будет хранить число не в двоичном виде, а в двоичном представлении.
Посмотрите на библиотеку, которая хранит числа в виде десятичного представления, я знаю, что Boost имеет один, http://svn.boost.org/svn/boost/sandbox/big_number/libs/multiprecision/doc/html/index.html, но есть много других.
Если это кажется чем-то тяжеловесным решением, попробуйте прочитать istringstream символ за раз, если это цифра, преобразовать его из символа в целое, а затем сохранить в массиве.
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];
}
Здесь происходит два отдельных преобразования: есть преобразование из текста в двойной (iss >> d
), и есть преобразование этого двойного в текст (std::cout << d
). Первый хранит наилучшее приближение входного текста в вашем double
переменная. Второй использует точность по умолчанию; оно округляет значение, чтобы соответствовать этой точности, и подавляет конечные нули. Вот почему вы видите 2.111
, Если вы хотите увидеть больше цифр, используйте setprecision
увеличить количество цифр в выводе. Как уже говорили другие, все, что больше, чем около 15 цифр (вход или выход), — чепуха.