Преобразовать float в строку с указанной точностью и количеством десятичных цифр?

88

Как преобразовать число с плавающей запятой в строку в C ++, указав точность и количество десятичных цифр?

Например: 3.14159265359 -> "3.14"

Шеннон Мэтьюз
источник
почему бы не создать временное число с плавающей запятой с заданной точностью, а затем преобразовать его в строку?
Gilad
@Gilad У 'temp float' нет 'заданной точности', и бывают случаи, когда такой подход не работает. Этот вопрос в основном похож на вопрос «что эквивалентно формату '% .2f'»?
user2864740
1
См. Stackoverflow.com/questions/14432043/c-float-formatting, если это просто необходимо для ввода-вывода.
user2864740

Ответы:

131

Типичный способ - использовать stringstream:

#include <iomanip>
#include <sstream>

double pi = 3.14159265359;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << pi;
std::string s = stream.str();

Смотрите исправленные

Используйте фиксированную запись с плавающей запятой

Устанавливает floatfieldфлаг формата для потока str равным fixed.

Если floatfieldзадано значение fixed, значения с плавающей запятой записываются с использованием записи с фиксированной запятой: значение представляется с точно таким количеством цифр в десятичной части, как указано в поле точности ( precision), и без экспоненты.

и установите точность .


Для преобразований технического назначения , таких как хранение данных в файле XML или JSON, C ++ 17 определяет семейство функций to_chars .

Предполагая совместимый компилятор (которого нам не хватало на момент написания), можно рассмотреть что-то вроде этого:

#include <array>
#include <charconv>

double pi = 3.14159265359;
std::array<char, 128> buffer;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), pi,
                               std::chars_format::fixed, 2);
if (ec == std::errc{}) {
    std::string s(buffer.data(), ptr);
    // ....
}
else {
    // error handling
}
AlexD
источник
28

Обычный метод для этого - «печать в строку». В C ++ это означает использование std::stringstreamчего-то вроде:

std::stringstream ss;
ss << std::fixed << std::setprecision(2) << number;
std::string mystring = ss.str();
Матс Петерссон
источник
12

Другой вариант snprintf:

double pi = 3.1415926;

std::string s(16, '\0');
auto written = std::snprintf(&s[0], s.size(), "%.2f", pi);
s.resize(written);

Демо . Следует добавить обработку ошибок, т.е. проверкуwritten < 0.

Коломбо
источник
Почему было snprintfбы лучше в этом случае? Пожалуйста, объясните ... Это, конечно, не будет быстрее, меньше кода [Я написал реализацию printf, он довольно компактен и содержит чуть меньше 2000 строк кода, но с расширениями макросов достигает примерно 2500 строк]
Матс Петерсон,
1
@MatsPetersson Я твердо верю, что это будет быстрее. Именно поэтому я и дал такой ответ.
Коломбо,
@MatsPetersson Я провел измерения (GCC 4.8.1, -O2), которые показывают, что snprintf действительно занимает меньше времени в 17/22 раз. Я скоро загрузю код.
Коломбо,
@MatsPetersson Мой тест, вероятно, ошибочен, но версия stringstream занимает в два раза больше времени, чем версия snprintf при 1000000 итерациях (Clang 3.7.0, libstdc ++, -O2).
Я тоже провел некоторые измерения - и может показаться (по крайней мере, в Linux), что и stringstream, и snprintf используют одну и ту же базовую __printf_fpфункциональность - довольно странно, что это занимает так много времени stringstream, как это действительно не должно быть.
Матс Петерссон,
9

Вы можете использовать fmt::formatфункцию из библиотеки {fmt} :

#include <fmt/core.h>

int main()
  std::string s = fmt::format("{:.2f}", 3.14159265359); // s == "3.14"
}

где 2точность.

Это средство форматирования было предложено для стандартизации в C ++: P0645 . И P0645, и {fmt} используют синтаксис строки формата, подобный Python, который похож на printfs, но используется в {}качестве разделителей вместо %.

витаут
источник
7

Здесь решение, использующее только std. Однако обратите внимание, что это только округление.

    float number = 3.14159;
    std::string num_text = std::to_string(number);
    std::string rounded = num_text.substr(0, num_text.find(".")+3);

Ибо roundedдает:

3.14

Код преобразует все число с плавающей запятой в строку, но вырезает все символы через 2 символа после "."

Себастьян Р.
источник
Единственное, таким образом вы фактически не округляете число.
ChrCury78
1
@ ChrCury78, как указано в моем ответе, это только округляет. Если вы хотите нормальное округление, просто добавьте 5 к цифре после вашего значения. Например number + 0.005в моем случае.
Себастьян Р.
2

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

float num=99.463;
float tmp1=round(num*1000);
float tmp2=tmp1/1000;
cout << tmp1 << " " << tmp2 << " " << to_string(tmp2) << endl;

Вы получаете

99463 99.463 99.462997

Примечание: переменная num может иметь любое значение, близкое к 99,463, вы получите такую ​​же распечатку. Дело в том, чтобы избежать удобной функции C ++ 11 "to_string". Мне потребовалось время, чтобы выбраться из этой ловушки. Лучше всего использовать методы stringstream и sprintf (язык C). C ++ 11 или новее должен предоставлять второй параметр в виде количества отображаемых цифр после плавающей запятой. Сейчас по умолчанию 6. Я утверждаю это, чтобы другие не тратили время на эту тему.

Я написал свою первую версию, дайте мне знать, если вы обнаружите ошибку, которую необходимо исправить. Вы можете контролировать точное поведение с помощью иоманипулятора. Моя функция - показать количество цифр после десятичной точки.

string ftos(float f, int nd) {
   ostringstream ostr;
   int tens = stoi("1" + string(nd, '0'));
   ostr << round(f*tens)/tens;
   return ostr.str();
}
Кемин Чжоу
источник