Форматирование n значащих цифр в C++ без научной записи

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

Спецификация формата %f не имеет дело со значительными цифрами, и %g иногда дает мне научную запись (что не подходит для моего использования).

Я хочу значения в форме "123", "12.3", "1.23" or "0.000000123",

Есть ли элегантный способ сделать это с помощью C++ или boost?

3 ответа

Решение

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

#include <string>
#include <math.h>
#include <sstream>
#include <iomanip>

int round(double number)
{
    return (number >= 0) ? (int)(number + 0.5) : (int)(number - 0.5);
}

std::string format(double f, int n)
{
    if (f == 0) {
        return "0";
    }            
    int d = (int)::ceil(::log10(f < 0 ? -f : f)); /*digits before decimal point*/
    double order = ::pow(10., n - d);
    std::stringstream ss;
    ss << std::fixed << std::setprecision(std::max(n - d, 0)) << round(f * order) / order;
    return ss.str();
}

C++11 имеет std::round, поэтому вам не понадобится моя версия с новым компилятором.

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

Он также удовлетворяет требованиям @Mats Petersson, поэтому будет работать во всех случаях.

Бит, который мне не нравится, - это начальная проверка на ноль (чтобы функция журнала не взорвалась). Предложения по улучшению / прямое редактирование этого ответа приветствуются.

std::fixed а также std::setprecision (а также <iomanip> в общем) ваши друзья.

std::cout << 0.000000123 << '\n';

печать 1.23e-07 а также

std::cout << std::setprecision(15) << std::fixed << 0.000000123 << '\n';

печать 0.000000123000000

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

std::cout << std::fixed << 123456789012345678901234567890.0 << '\n';

распечатает 123456789012345677877719597056.000000 (вероятно, не то, что вы хотите)

Я думаю, вам придется удалить конечные нули самостоятельно:

string trimString(string str)
{
    string::size_type s;
    for(s=str.length()-1; s>0; --s)
    {
        if(str[s] == '0') str.erase(s,1);
        else break;
    }
    if(str[s] == '.') str.erase(s,1);
    return str;
}

Использование:

double num = 0.000000123;
stringstream ss;
ss << num;
ss.str("");
ss << std::setprecision(15) << std::fixed << num; // outputs 0.000000123000000
string str;
ss >> str;
str = trimString(str);
cout << str << endl;  // outputs 0.000000123

Собрать вместе:

string format(int prec, double d) {
    stringstream ss;
    ss << d;
    ss.str("");
    ss << std::setprecision(prec) << std::fixed << d;

    string str;
    ss >> str;
    string::size_type s;
    for(s=str.length() - 1; s > 0; --s)
    {
        if(str[s] == '0') str.erase(s,1);
        else break;
    }
    if(str[s] == '.') str.erase(s,1);
    return str;
}

Использование:

double num = 0.000000123;
cout << format(15, num) << std::endl;

Если кто-то знает лучший способ...

Другие вопросы по тегам