Не нашёл как с наименьшей потерей точности выводить double в "читабельном" виде.
Написал свой велосипед.
Покритикуйте или предложите свой вариант.
Если обнаружите ошибки — буду рад узнать, код сырой (не тестированный).
Основные требования:
— точность не меньше 10 знаков;
— "читабельный" вид.
Под читабельным видом понимается что количество первая значащая цифра расположена до запятой/после запятой не дальше некоторого расстояния (например, 6/3. устроит и другой вариант) и нет незначащих нулей.
Можете также предложить свои критерии читабельности.
#include "stdafx.h"
#include <string>
#include <iostream>
#include <sstream>
#include <math.h>
enum e_parse_state {
PS_FIND_E_AND_COPY,
PS_FIND_0_AND_SKIP,
PS_COPY,
};
// функкция преобразуется double в строку с наименьшей потерей точности
// и представляет число в наиболее привычном виде, т.е:
// - слишком большие и маленькие числа представлятся в "научном" формате;
// - убраны незначащие нули.
std::string double2string( const double& t ) {
// диапазон для чисел, которые должны выводиться без экспоненты
const double NON_EXP_MIN=1e-3;
const double NON_EXP_MAX=1e6;
const int maxPrecision=20+abs(int(log10(NON_EXP_MIN)));
//
std::ostringstream ost;
ost.precision(maxPrecision);
bool isExp=fabs(t)<NON_EXP_MIN || NON_EXP_MAX<fabs(t);
if(isExp) {
ost << std::scientific;
} else {
ost << std::fixed;
}
ost << t;
std::string s=ost.str();
// убираем незначащие нули
std::string res;
int state=isExp?PS_FIND_E_AND_COPY:PS_FIND_0_AND_SKIP;
for(int i=s.size()-1;i>=0;--i) { // с конца строки
bool doAddSymbol=true;
char c=s[i];
switch(state) {
case PS_FIND_E_AND_COPY:
if(c=='e') {
state=PS_FIND_0_AND_SKIP;
}
break;
case PS_FIND_0_AND_SKIP:
if(c=='0') {
doAddSymbol=false;
} else {
state=PS_COPY;
if(c=='.') {
doAddSymbol=false;
}
}
break;
case PS_COPY:
default:
// ничего не делаем
break;
}
if(doAddSymbol) {
res=c+res;
}
}
return res;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<double2string(1e16)<<std::endl;
std::cout<<double2string(1e-5)<<std::endl;
std::cout<<double2string(10.)<<std::endl;
std::cout<<double2string(1.23456789123456789123456789)<<std::endl;
std::cout<<double2string(0.00123456789123456789123456789)<<std::endl;
std::cout<<double2string(123456789123456789.)<<std::endl;
std::cout<<double2string(123456789.123456789)<<std::endl;
std::cout<<double2string(1e-4)<<std::endl;
std::cout<<double2string(1.)<<std::endl;
std::cout<<double2string(3.5)<<std::endl;
std::cout<<double2string(-0.01234501)<<std::endl;
return 0;
}
Вывод программы:
1e+016
1.0000000000000001e-005
10
1.2345678912345679
0.0012345678912345679
1.2345678912345678e+017
1.2345678912345679e+008
1e-004
1
3.5
-0.01234501
PS
1.0000000000000001e-005 — плохой вариант, но как исправить не знаю.