2017-08-16 31 views
0

我有一個double,我想以儘可能無損的方式存儲在一個字符串中,但存儲格式的最大字符串長度爲16,這使得這不可能(因爲std::numeric_limits<double>::max_digits10表示我們需要17精度)。因此,佔字符-.的可能性,我們可能就如下鉗位到14或15:保證默認輸出格式的最大輸出長度爲double setprecision

#include <sstream> 
#include <limits> 
#include <iomanip> 

template <typename T> 
std::string format(T val) 
{ 
    int precision = std::min(std::numeric_limits<T>::max_digits10, 
    val >= 0.0 ? 15 : 14); 
    std::ostringstream oss; 
    oss.precision(precision); 
    oss << val; 
    return oss.str(); 
} 

然而,默認格式輸出可以在科學記數法,其結果可能仍然超限邊界。例如,該輸入:

7.105427357601002e-14

需要的精度降低至12,它提供了足夠短的輸出之前:

7.1054273576e-14

所以,我怎麼能計算最大精度將保持輸出在我的長度約束沒有暴力的方法,如重試較低的精度,如果結果太長?

+0

你是不是想左(右)修剪長雙文字,圓它,或根據模板類型的數字限制? – Ron

+1

@Ron的問題基本上是:「鑑於我的存儲(以字節爲單位)的大小,我怎麼打印'double'爲最大精度可能適合在長度」 – Justin

+0

元的問題:有多少精做你的實際需要爲您的應用程序?多少(相對/絕對)錯誤是可以接受的?你確定計算錯誤並不占主導地位嗎? –

回答

0

如@bsruth提到的,默認格式是相當於%gfprintf,和與此行爲指定:

令P等於精度如果非零,6如果不是指定精度,或者如果精度爲0,則爲1。
然後,如果用式E中的轉換將具有X的指數:

如果P> X≥-4,轉換是具有式F或F和精度p - 1 - X.
否則,轉換風格e或E和精度p - 1

因此,如果需要,我們計算base10指數,因爲它是在科學記數法並用它來調整精度下降:

#include <cmath> 
#include <limits> 
#include <string> 
#include <sstream> 

double frexp10_scientific(double arg, int* exp) 
{ 
    *exp = (arg == 0) ? 0 : static_cast<int>(std::log10(std::fabs(arg))); 
    double fraction = arg * pow(10 , -(*exp)); 
    // adjust to scientific format decomposition 
    if (std::fabs(fraction) < 1.0) { 
     *exp -= 1; 
     fraction *= 10; 
    } 
    return fraction; 
} 

template <typename T> 
int precision_needed(T val) 
{ 
    // initial guess on precision 
    int precision; 
    if (val >= 0.0) { 
    if (val >= 1e15 && val < 1e16) 
     precision = 16; // all integer digits used 
    else 
     precision = 15; // '.' present 
    } else { 
    if (val > -1e15 && val <= -1e14) 
     precision = 15; // '-' present, all integer digits used 
    else 
     precision = 14; // '-' and '.' present 
    } 

    int exp; 
    frexp10_scientific(val, &exp); 

    // adjust for scientific notation if needed 
    if (exp < -99) { 
    precision -= 5; // "e-123" 
    } else if (exp < -4) { 
    precision -= 4; // "e-01" 
    } else if (exp >= precision) { 
    if (exp > 99) { 
     precision -= 5; // "e+123" 
    } else { 
     precision -= 4; // "e+12" 
    } 
    } 

    return std::min(precision, std::numeric_limits<T>::max_digits10); 
} 

template <typename T> 
std::string format(T val) { 
    std::ostringstream oss; 
    oss.precision(precision_needed(val)); 
    oss << val; 
    return oss.str(); 
} 

一些test cases,顯示輸入,精密,長和輸出:

1.123e-4: [p=15,l=9] 0.0001123 
1.123e-5: [p=11,l=9] 1.123e-05 
1.123e-99: [p=11,l=9] 1.123e-99 
1.123e-100: [p=10,l=10] 1.123e-100 
7.105427357601002e-14: [p=11,l=16] 7.1054273576e-14 
7.105427357621342e-14: [p=11,l=16] 7.1054273576e-14 
-7.105427357601002e-14: [p=10,l=16] -7.105427358e-14 
-7.105427357621342e-14: [p=10,l=16] -7.105427358e-14 
7.105427357621342e14: [p=15,l=15] 710542735762134 
7.105427357621342e15: [p=16,l=16] 7105427357621342 
7.105427357621342e16: [p=11,l=16] 7.1054273576e+16 
-7.105427357621342e14: [p=15,l=16] -710542735762134 
-7.105427357621342e15: [p=10,l=16] -7.105427358e+15 
7.105427357621342e15: [p=16,l=16] 7105427357621342 
7.105427357621342e16: [p=11,l=16] 7.1054273576e+16 
-7.105427357621342e15: [p=10,l=16] -7.105427358e+15 
1.123e14: [p=15,l=15] 112300000000000 
1.123e15: [p=16,l=16] 1123000000000000 
1e15: [p=16,l=16] 1000000000000000 
1e16: [p=11,l=5] 1e+16 
-1e15: [p=10,l=6] -1e+15 
-1e14: [p=15,l=16] -100000000000000 
1.23456789e-14: [p=11,l=16] 1.2345678901e-14