Конвертировать float в string без потери точности - PullRequest
14 голосов
/ 16 декабря 2010

Я хочу сохранить значение с плавающей точкой в ​​строке без потери или добавления каких-либо цифр с одинарной точностью.

Например, если мое значение с плавающей точкой равно 23.345466467, я хочу, чтобы в моей строке были точные цифры str = "23.345466467".

Я пытался использовать функцию формата CString с% f.Это дает только первые 6 точности.или если я использую% 10, если мое значение с плавающей запятой имеет точность меньше 10, это добавляет еще большую точность мусора.Я хочу получить точное значение с плавающей точкой в ​​моей строке.как это сделать?

Ответы [ 10 ]

9 голосов
/ 03 мая 2014

См. Подробное обсуждение в http://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/.

Краткий ответ: минимальная точность следующая:

printf("%1.8e", d);  // Round-trippable float, always with an exponent
printf("%.9g", d);   // Round-trippable float, shortest possible
printf("%1.16e", d); // Round-trippable double, always with an exponent
printf("%.17g", d);  // Round-trippable double, shortest possible

Или, что то же самое, с std::ostream& os:

os << scientific << setprecision(8) << d;    // float; always with an exponent
os << defaultfloat << setprecision(9) << d;  // float; shortest possible
os << scientific << setprecision(16) << d;   // double; always with an exponent
os << defaultfloat << setprecision(17) << d; // double; shortest possible
8 голосов
/ 16 декабря 2010

C99 поддерживает формат %a в printf, что позволяет выводить содержимое двойного без потери точности.

8 голосов
/ 16 декабря 2010

Это будет зависеть от того, является ли ваше значение с плавающей запятой 23.345466467 точно представимым (вероятно, нет)

Что должен знать каждый специалист по вычислительной технике об арифметике с плавающей точкой

Почему числа с плавающей точкой могут потерять точность

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

[ Не проверено : вы можете попробовать выполнить приведение к двойному типу, а затем использовать "% d".дополнительные «защитные» цифры, но они не будут работать для всех значений]

2 голосов
/ 16 декабря 2010

Другие уже прокомментировали, что 23.345466467 не существует как число с плавающей точкой, но если ваша цель - это просто преобразование значений с плавающей точкой, которые действительно существуют, без случайного изменения их значения, вы можете использовать snprintf и strtodпо крайней мере, с DECIMAL_DIG местами (POSIX, но не обычный ISO C, гарантирует точность в обоих направлениях) или выведите число с плавающей точкой в ​​шестнадцатеричном, а не в десятичном виде.C99 имеет спецификатор формата %a для печати чисел с плавающей точкой в ​​шестнадцатеричном формате, но если вы не можете зависеть от C99, легко написать свой собственный.В отличие от десятичного, наивный алгоритм печати шестнадцатеричных чисел работает просто отлично.Это выглядит следующим образом:

  1. Масштабирование степенью 2 для перевода значения в диапазон 0x8 <= val < 0x10.Эта степень 2 становится показателем степени в вашем результате.
  2. Повторно удаляйте целую часть val и умножайте на 0x10, чтобы получить выходные цифры.
  3. Когда val достигает 0, все готово.

Преобразование в обратном направлении также легко.

2 голосов
/ 16 декабря 2010

Количество (десятичных) цифр, которое вам нужно, чтобы уникально обозначало любое float по определению std::numeric_limits<float>::maxdigits10. Формат float не может различать 0.0 и 0.0000, поэтому эта константа является максимальным значением, которое вам когда-либо понадобится.

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

[править] std::numeric_limits<float>::digits10 представляет обратную концепцию: наибольшее количество десятичных цифр, такое, что десятичное число -> двоичное число -> десятичное число гарантировано точно.

0 голосов
/ 17 мая 2014

ОП хочет C или C ++, но если вам понадобится решение C #, используйте строку форматирования чисел "R", например:

float x = 23.345466467f;
string str = x.ToString("R");

Обратите внимание, что «R» - это сокращение от «туда-обратно». Больше информации о MSDN .

0 голосов
/ 16 декабря 2010

Храните ли вы его, чтобы потом перечитать и использовать как поплавок? Или тебя волнует, что говорит строка? Как уже говорили другие, вам нужно написать двоичное представление, а не основание10. Или вы можете быть немного хитрым и сделать что-то вроде этого

float f=23.345466467
int i=*(int*)&f; //re-interpret the bits in f as an int
cout << i;

и прочитать

int i;
cin >> i;
float f=*(float&)&i;

[Стандартный отказ от переносимости и проверка того, что значения int и float на вашей платформе одинаковы.]

Таким образом, вы можете вывести значение и прочитать его обратно без потери точности. Но он не читается человеком, когда хранится.

0 голосов
/ 16 декабря 2010

Лучший способ - сохранить его двоичное представление, как предлагает @pgm. Однако вы также можете сохранить его в десятичной записи, но с использованием записи периода, например:

23.5095446(1545855463)

Таким образом, они могут храниться как-то «без потерь», но это потребует очень осторожного кодирования.

0 голосов
/ 16 декабря 2010

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

Для сохранения наилучшей точности для типа с плавающей запятой:

std::string convert(float value)
{
  std::stringstream ss;
  ss << std::setprecision(std::numeric_limits<float>::digits10+1);
  ss << value;
  return ss.str();
}

Или более универсальный

template<typename FloatingPointType>
std::string convert(FloatingPointType value)
{
  std::stringstream ss;
  ss << std::setprecision(std::numeric_limits<FloatingPointType>::digits10+1);
  ss << value;
  return ss.str();
}

Это обрезает цифры, которые вам не нужны, но сохранит максимально возможную точность.

0 голосов
/ 16 декабря 2010

Возможно, вы используете fprintf () (from), чтобы преобразовать ваш float в строку. Но точность может быть потеряна сразу после воздействия, потому что «flaot» и «double» имеют ограниченную точность. Кто-то должен это подтвердить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...