2013-02-19 4 views
7

In base allo ios_base manipulators, ho fondamentalmente la scelta tra defaultfloat e fixed durante la formattazione di numeri in virgola mobile senza notazione esponenziale (con numeri decimali).Come si formatta un valore in virgola mobile in modo che non usi mai la notazione esponenziale né abbia zeri finali?

Tuttavia, voglio scegliere il maximum precision che produrrebbe un sacco zeri finali per fixed per molti numeri (ad esempio 1.), ma evitare di mai usare la notazione esponente. Se impostato su defaultfloat, sembrerà a destra la maggior parte del del tempo, a meno che il valore sia davvero molto piccolo, ma non lo sia 0.. In questo caso, la rappresentazione predefinita passa alla notazione scientifica da sola, che interrompe il destinatario dell'output formattato (poiché non ha idea di cosa significa 2.22045e-16.

Quindi, come posso avere la mia torta e mangiarla, anche io ? Cioè, non esponente notazione senza zeri trascinamento inutili


. Nota: non ho testare l'effetto della bandiera defaultfloat, dato che il mio gcc non sembra di attuare tale bandiera (ancora), ma presumo che sia l'impostazione predefinita che si applica senza utilizzare alcun flag. Ho controllato lo fixed, che si comporta come previsto.

+0

Che cosa si desidera stampare esattamente quando l'output è 2.22045e-16? '0' o' 0.00000000000000000222045' [non sono sicuro di aver ottenuto il giusto numero di zeri, ma faccio finta che sia ...] –

+0

@MatsPetersson: il secondo. – bitmask

+0

E se il valore è 16.00001210121019, lo si vuole esaxcamente, ma se il valore è 16.5, lo si desidera, vero? Penso che tu stia meglio scrivendo il tuo "float to string". Non è terribilmente difficile. –

risposta

1

Dal momento non sembra essere un modo corretto per farlo con lo stdlib, ecco il mio wrapper.

template <typename T> 
    struct FpFormat { 
    template <typename Stream> 
    static Stream& setfmt(Stream& str) { 
     return str; 
    } 
    template <typename String> 
    static String const& untrail(String const& str) { 
     return str; 
    } 
    }; 
    template <typename T> 
    struct FpFormatFloats { 
    template <typename Stream> 
    static auto setfmt(Stream& str) -> decltype(str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10)) { 
     return str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10); 
    } 
    template <typename String> 
    static String untrail(String str) { 
     if (str.find('.') == String::npos) 
     return str; 
     return ([](String s){ 
     return String(s.begin(),s.begin()+((s.back() == '.')?(s.size()-1):s.size())); 
     })(str.substr(0,(str+"0").find_last_not_of('0')+1)); 
    } 
    }; 
    template <> struct FpFormat<float> : FpFormatFloats<float> {}; 
    template <> struct FpFormat<double> : FpFormatFloats<double> {}; 
    template <> struct FpFormat<long double> : FpFormatFloats<long double> {}; 

    template <typename T> 
    std::string toString(T x) { 
    std::stringstream str; 
    FpFormat<T>::setfmt(str) << x; 
    return FpFormat<T>::untrail(str.str()); 
    } 
+0

Purtroppo, questo hardcode '.' (vedere http: //coliru.stacked-crooked .com/a/b72ae760073d8832) –

+0

Mi rendo conto che hai accettato la tua risposta in modo che questo codice funzioni per te ma in realtà non soddisfa il requisito di "notazione esponenziale mai usata" (prova numeri molto grandi o molto piccoli). La risposta di Mats è più vicina a colpirlo. Sto solo scrivendo questo commento per informare gli altri che hanno bisogno della funzione non-esponente. – slajoie

0

Non esiste una formattazione incorporata che faccia questo per voi. È possibile produrre il proprio formattatore da zero o scriverne uno che elabora la stringa per rimuovere gli zero finali non necessari.

Per esempio si potrebbe fare usando la sostituzione regex:

#include <iomanip> 
#include <iostream> 
#include <sstream> 
#include <regex> 

// not thoroughly tested 
std::string format(double d, int precision = 100) { 
    std::stringstream ss; 
    ss << std::fixed << std::setprecision(precision); 
    ss << d; 

    return std::regex_replace(
     std::regex_replace(ss.str(), std::regex(R"((-?\d*\.\d*?)0*$)"), "$1"), 
     std::regex(R"(\.$)"), ""); 
} 

int main() { 
    std::cout << format(-1) << '\n'; 
    std::cout << format(1e20) << '\n'; 
    std::cout << format(1e-20) << '\n'; 
    std::cout << format(2.22045e-16) << '\n'; 
} 

Pur utilizzando un'espressione regolare non è probabilmente una soluzione particolarmente efficiente.

+0

Purtroppo, questo hardcode '.' (vedi http://coliru.stacked-crooked.com/a/b72ae760073d8832) –

3

Un metodo semplice sarebbe qualcosa di simile a questo:

std::string float2string(double f) 
{ 
    std::stringstream ss; 

    ss << std::fixed << std::setprecision(122) << f; // 122 is LARGE, but you may find that really tiny or really large numbers still don't work out... 

    std::string s = ss.str(); 

    std::string::size_type len = s.length(); 

    int zeros = 0; 
    while(len > 1 && s[--len] == '0') 
     zeros++; 
    if (s[len] == '.') // remove final '.' if number ends with '.' 
     zeros++; 
    s.resize(s.length()-zeros); 


    return s; 
} 

ho dato qualche test. Il problema più grande è che dà un enorme numero di decimali per alcuni numeri, e cose del genere 0,05 esce come 0.05000000000000000277555756156289135105907917022705078125 e 0.7 diventa: 0.05000000000000000277555756156289135105907917022705078125

Ecco perché non è un ' "esatta" numero in forma binaria.

Penso che la soluzione sia calcolare approssimativamente quante cifre si desidera nel risultato, prendendo il numero di cifre intere (o di piano (log (f)/log (10) +1)) e quindi sottraendo quel numero da una costante, ad esempio 17, che è il numero di cifre che puoi aspettarti da un doppio. Quindi rimuovere gli zeri in eccesso.

Lascio che per il momento, come ho avuto qualche altre cose da fare che avrei dovuto iniziare un po 'di tempo fa ...;)

+0

Grazie. +1 per lo sforzo, ma [la mia versione] (http://stackoverflow.com/a/14966914/430766) sembra trattare correttamente tali numeri. – bitmask

+0

Tristemente, questo hardcode '.' (vedi http://coliru.stacked-crooked.com/a/b72ae760073d8832) –

+0

Lo std :: risolto ha funzionato per i miei bisogni, grazie. –

Problemi correlati