2016-01-25 7 views
4

У меня есть double x, который я хотел бы напечатать в виде строки s. Я хотел бы представление, чтобы соблюдать следующие свойства:Краткая и точная печать двухместных номеров

  • Точная: абсолютное значение (x - atof(s)) составляет менее эпсилон
  • Сжатый: представление не включает в себя более значащих цифр, чем требуется для быть точным

Каков наилучший способ для этого?

Для того, чтобы проиллюстрировать, почему %f и %g не делают работу:

printf("%f\n", 1.0);    // 1.000000  not concise 
printf("%g\n", 1.0);    // 1    good 
printf("%f\n", 4357890.2342389); // 4357890.234239 good 
printf("%g\n", 4357890.2342389); // 4.35789e+06 not accurate 
+1

'% .16g' или что-то подобное ? – cpplearner

+0

@cpplearner Классный, отлично работает. Если вы ответите, я приму это. – dshin

+0

Хотя «% .16g» хорошо работает для моих приведенных выше примеров, он технически не дает моего «краткого» определения для чего-то вроде «1.0/3», если epsilon, скажем, 1e-6. Решение подходит для моих нужд, но я хочу отметить это, если у других есть разные требования. – dshin

ответ

4

Вы можете использовать в качестве (cpplearner упомянутого) спецификатора формата %.16g для Printf или сделать это с std::cout и std::setprecision так:

#include <iostream> 
#include <iomanip> 

int main() 
{ 
    std::cout << 4357890.2342389 << std::endl; 

    std::cout << std::setprecision(16); 

    std::cout << 4357890.2342389 << std::endl; 
    std::cout << 1.0 << std::endl; 

    std::cout << std::fixed; 
    std::cout << "fixed\n"; 

    std::cout << 4357890.2342389 << std::endl; 
} 

, который производит:

4.35789e+06 
4357890.2342389 
1 
fixed 
4357890.2342389002442360 

Интернет код link

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

Рекомендуемая литература: What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

Как я отмечаю в комментарии к OP, это решение хорошо работает для моих нужд, но технически не удается выполнить мое «сжатое» определение для чего-то вроде «1.0/3», если epsilon - это, скажем, 1е-6. Принимая во внимание это, если кто-то действительно желает требований, которые я точно изложил в ОП. – dshin

0

Ну вы можете настроить спецификатор формата, как описано здесь:

Format specifiers in C

Поскольку вы мечения это как C++, почему бы не попробовать:

#include <iostream> 

std::cout << double(1.0) << std::endl 
std::cout << 1.0 << std::endl 
std::cout << double(4357890.2342389) << std::endl  
std::cout << 4357890.2342389 << std::endl 

если вы хотите больше точности вы можете

std::cout << std::setw(32) << std::setprecision(16) << double(1.0); 

почему приводит напечатать больше символов вашего номера.

+0

В чем причина приведения двойных значений к типу double? –

+0

Создает ли 'iostream' лучшие результаты, чем' printf'? Если да, то какие аспекты улучшены - точнее ли это? Более кратким? И то и другое? – anatolyg

+0

Это путь C++ к этим вещам. Результат должен быть примерно таким же, но вам не нужно предоставлять спецификаторы формата. Если вы измените свой тип данных, в ваших потоковых вызовах не потребуются обновления спецификатора формата. –

1

Для удовлетворения ваших требований вы можете сделать что-то, как уродливый, как это:

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

std::string concise_and_accurate(double x, int prec) { 
    // print the number to a stringstream with all the figures needed 
    std::stringstream strnum; 
    strnum << std::fixed << std::setprecision(prec) << x; 
    std::string str = strnum.str(); 
    // cancel the extra zeroes at the right 
    str.erase(str.find_last_not_of('0') + 1, std::string::npos); 
    // delete the '.' if there are no other digits at the right 
    if (str[str.size()-1] == '.') str.erase(str.size()-1); 
    return str; 
} 


int main() { 
    double test[] = { 4357890.2342389, 1.0, 1.0/3, 100.0/3 }; 

    for (auto x : test) { 
     std::cout << concise_and_accurate(x,6) << '\n'; 
    } 

    return 0; 
} 

Выход:

4357890.234239 
1 
0.333333 
33.333333 
+0

Героическое усилие! В нашей системе на самом деле используется форматирование в стиле printf, при этом преобразования строк выполняются в отдельном потоке с низким приоритетом по соображениям производительности. Я мог бы в конечном итоге принять ваше решение, используя пользовательские% formatters, но сначала мне нужно решить [эту проблему] (http://stackoverflow.com/questions/16334179/custom-support-for-attribute-format), которая имеет были в нашем списке TODO на некоторое время. – dshin

+0

@dshin Я всегда думал, что весь смысл '' должен был избавиться от всех% форматировщиков и предоставить тип безопасного буферизованного ввода-вывода. Разумеется, это стоило дорого. Вы измерили разницу в производительности двух методов, пытающихся минимизировать вызовы записи (несколько cout с большими конкатенированными строками)? –

+0

Да, cout слишком медленный для наших требований к производительности. Исходный файл printf также будет слишком медленным, так как стоимость 'printf (« действительно длинная форматная строка% d \ n », 10)» масштабируется с длиной строки формата. Наша система просто копирует адрес строки формата вместе с аргументами в буфер. – dshin

Смежные вопросы