istringstream округляет длинные числа — как я могу предотвратить это?

Учитывая следующий код:

istringstream i("2.11099999999999999999");

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

cout << d << endl;

Выход 2.111 ,

Я хочу иметь возможность работать с длинными числами, числами с плавающей запятой (включая числа с плавающей запятой), однако при конвертации istringstream чтобы удвоить, я получаю округленное число.

Как я могу предотвратить это? Как я могу сохранить данный ввод как есть?

С уважением

0

Решение

В этом случае вы не можете предотвратить это. 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 не является конечной двоичной дробью по той же причине, что одна треть не является конечной десятичной дробью.

Таким образом, существует два способа сохранить данный ввод как есть (то есть точно представить это число):

  1. не конвертировать в doubleоставь это как строку.
  2. не конвертировать в doubleвместо этого найдите или напишите библиотеку, которая представляет десятичные дроби и / или рациональные числа. Например, библиотека GMP имеет mpq_t или есть Boost.Rational.
2

Другие решения

В пространстве математики существует бесконечное число чисел и double конечно, то есть он вписывается в 64 бита, поэтому только подмножество таких значений представляется точно. И фактически 2.111 также точно не представлен, потому что, хотя числа печатаются с десятичными точками, под ними действительно используется двоичный код, который представляет собой мантиссу (52 бита точности) и экспоненту со знаком плюс.

Так как мантисса имеет точность около 52 битов, 2 к степени 52 составляет 4,503,599,627,370,496

Как видите, это число имеет 15 цифр после начальных 4, таким образом, вы получите 15 цифр с десятичной точностью.

Для большинства целей эта точность достаточно хороша. В тех случаях, когда вам нужна большая точность, или в особых случаях, которые требуют большого количества и небольших изменений, вам необходимо принять специальные методы. Есть несколько библиотек, которые обеспечат это для вас.

0

как сказано, вы не можете иметь такую ​​точность.
Вы всегда можете увеличить количество отображаемых цифр, как описано здесь: Повысить точность

0

Вам нужно будет хранить число не в двоичном виде, а в двоичном представлении.

Посмотрите на библиотеку, которая хранит числа в виде десятичного представления, я знаю, что 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];
}
0

Здесь происходит два отдельных преобразования: есть преобразование из текста в двойной (iss >> d), и есть преобразование этого двойного в текст (std::cout << d). Первый хранит наилучшее приближение входного текста в вашем double переменная. Второй использует точность по умолчанию; оно округляет значение, чтобы соответствовать этой точности, и подавляет конечные нули. Вот почему вы видите 2.111, Если вы хотите увидеть больше цифр, используйте setprecision увеличить количество цифр в выводе. Как уже говорили другие, все, что больше, чем около 15 цифр (вход или выход), — чепуха.

0
По вопросам рекламы [email protected]