Подтвердить что ты не робот

Преобразование float в строку без потери точности

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

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

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

4b9b3361

Ответ 1

В первую очередь: вы не можете преобразовать float в строку и обратно, не потеряв немного или два. Поскольку это не преобразование 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();
}

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Ответ 4

См. подробное обсуждение в 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

Ответ 5

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

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

23.5095446(1545855463)

Таким образом, они могут быть как-то "без потерь", но для этого потребуется очень тщательное кодирование.

Ответ 9

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

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 имеют одинаковый размер на вашей платформе.]

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

Ответ 10

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

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

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