Точность C ++: строка удваивается - PullRequest
4 голосов
/ 08 июля 2010

У меня проблема с точностью до двойного после выполнения некоторых операций над преобразованной строкой в ​​двойное число.

#include <iostream>   
#include <sstream>
#include <math.h>

using namespace std;

// conversion function
void convert(const char * a, const int i, double &out)
{

   double val;

   istringstream in(a);
   in >> val;

   cout << "char a -- " << a << endl;
   cout << "val ----- " << val << endl;

   val *= i;

   cout << "modified val --- " << val << endl;
   cout << "FMOD ----- " << fmod(val, 1) << endl;

   out = val;

   return 0;

}

Это не относится ко всем числам, введенным в виде строки, поэтому ошибка не является постоянной. Он влияет только на некоторые числа (34,38 кажется постоянным).

В тот момент, когда я передаю a = 34.38 и i = 100, он возвращает это:

char a -- 34.38
Val ----- 34.38
modified val --- 3438
FMOD ----- 4.54747e-13

Это сработает, если я изменю Val на float, так как точность ниже, но мне нужно удвоить.

Это также repro, когда я использую atof, sscanf и strtod вместо sstream.

В C ++, как лучше всего преобразовать строку в двойное и на самом деле вернуть точное значение?

Спасибо.

Ответы [ 2 ]

11 голосов
/ 08 июля 2010

Это почти точная копия многих вопросов здесь - в основном, нет точного представления 34,38 в двоичной с плавающей запятой, поэтому ваши 34 + 19/50 представлены как 34 + k / n, где n - степеньдва, и нет точной степени двойки, которая имеет 50 как фактор, поэтому точного значения k невозможно.

Если вы установите точность вывода, вы увидите, что наилучшее двойное представление не является точным:

cout << fixed << setprecision ( 20 );

дает

char a -- 34.38
val ----- 34.38000000000000255795
modified val --- 3438.00000000000045474735
FMOD ----- 0.00000000000045474735

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

При использовании чисел с плавающей точкой умножение оказывается округленным, а не вверх, так что вы получитеТочный результат.Это не поведение, от которого вы можете зависеть.

2 голосов
/ 08 июля 2010

«Проблема» в том, что 34.38 не может быть точно представлено с плавающей запятой двойной точности.Вы должны прочитать эту статью , которая описывает, почему невозможно представить десятичные значения точно в плавающей запятой.

Если вы должны были проверить "34.38 * 100" в шестнадцатеричном формате (согласно "формату hex")например, в MATLAB) вы увидите:

40aadc0000000001

Обратите внимание на последнюю цифру.

...