Преобразуйте double в строку, быстро соблюдая заданную точность

У меня есть программа C++, использующая SDL. Во время рендеринга мне нужно нарисовать некоторые графические компоненты. Иногда мне нужно преобразовать double переменных, округленных до одного десятичного знака, в std::string.

Для этого я сейчас использую объект ostringstream, и он отлично работает.

std::ostringstream ss;
ss << std::fixed << std::setprecision(1) << x;

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

Я попытался округлить переменные double с помощью std::to_string(std::round(x * 10) / 10)), но это не сработало — я все равно получил вывод, похожий на 2.500000000.

  • Есть ли другое решение?
  • Стоит ли ostringstream большие расходы?

person Grégoire Borel    schedule 12.01.2016    source источник
comment
учитывая производительность: ostringstream будет использовать locale, который вызывает некоторые накладные расходы, в противном случае, если вы повторно используете один ostringstream во время вызова рендеринга (однократное выделение / увеличение внутреннего буфера), тогда не должно быть больше накладных расходов, чем sprintf   -  person BeyelerStudios    schedule 12.01.2016
comment
но это не сработало, вы должны уточнить это.   -  person NathanOliver    schedule 12.01.2016
comment
На всякий случай, если вы действительно имеете в виду, что это не сработало, вы делали компиляцию с помощью компилятора C++11 или ваш компилятор настроен на (как минимум) стандарт C++11 , не так ли?   -  person DevSolar    schedule 12.01.2016
comment
Я скомпилировал компилятором С++ 11. Ничего не изменил. У меня все еще была установлена ​​переменная 2.500000000 (например).   -  person Grégoire Borel    schedule 12.01.2016
comment
@ Q3SanD: Что ж, std::setprecision точно переворачивает этот вопрос с ног на голову. Удалил мой ответ, так как оказалось, что он лает не по тому дереву.   -  person DevSolar    schedule 12.01.2016
comment
Спасибо за редактирование. Означает ли это, что я остался с этим решением? Могу ли я использовать решение, которое я пробовал с std::round, а затем использовать std::string?   -  person Grégoire Borel    schedule 12.01.2016


Ответы (1)


Вы не можете указать точность с помощью std::to_string, так как это прямой эквивалент printf с параметром %f (при использовании double).

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

#include <iostream>
#include <sstream>
#include <iomanip>

std::string convertToString(const double & x, const int & precision = 1)
{
    static std::ostringstream ss;
    ss.str(""); // don't forget to empty the stream
    ss << std::fixed << std::setprecision(precision) << x;

    return ss.str();
}


int main() {
    double x = 2.50000;

    std::cout << convertToString(x, 5) << std::endl;
    std::cout << convertToString(x, 1) << std::endl;
    std::cout << convertToString(x, 3) << std::endl;

    return 0;
}

Он выводит (см. на Coliru):

2.50000

2.5

2.500

Однако я не проверял производительность... но я думаю, что вы могли бы добиться большего, инкапсулировав это в класс (например, вызывать std::fixed и std::precision только один раз).

В противном случае вы все равно могли бы использовать sprintf с параметрами, которые вас устраивают.


Идя немного дальше, с инкапсулирующим классом, который вы можете использовать как статический экземпляр или член другого класса... как хотите (Посмотреть на Coliru).

#include <iostream>
#include <sstream>
#include <iomanip>

class DoubleToString
{
public:
    DoubleToString(const unsigned int & precision = 1)
    {
        _ss << std::fixed;
        _ss << std::setprecision(precision);
    }

    std::string operator() (const double & x)
    {
        _ss.str("");
        _ss << x;
        return _ss.str();
    }

private:
    std::ostringstream _ss;

};


int main() {
    double x = 2.50000;

    DoubleToString converter;

    std::cout << converter(x) << std::endl;

    return 0;
}

Другое решение без использования ostringstream (Посмотреть на Coliru):

#include <iostream>
#include <string>
#include <memory>

std::string my_to_string(const double & value) {
  const int length = std::snprintf(nullptr, 0, "%.1f", value);

  std::unique_ptr<char[]> buf(new char[length + 1]);
  std::snprintf(buf.get(), length + 1, "%.1f", value);

  return std::string(buf.get());
}

int main(int argc, char * argv[])
{

    std::cout << my_to_string(argc) << std::endl;
    std::cout << my_to_string(2.5156) << std::endl;

}
person dkg    schedule 12.01.2016
comment
Он по-прежнему использует ostringstream (думаю, выбора нет), но он намного чище, чем мое решение. Я не буду отмечать это как ответ, но я проголосую за него. Спасибо. - person Grégoire Borel; 15.01.2016
comment
@ Q3SanD Я добавил еще одно решение без ostringstream, но я не уверен, что оно более эффективно, вам следует запустить стенд. - person dkg; 15.01.2016