double в строку точно и красиво
От: Аноним  
Дата: 28.07.10 12:37
Оценка:
Не нашёл как с наименьшей потерей точности выводить 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 — плохой вариант, но как исправить не знаю.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.