2013-02-19 7 views
7

Zgodnie z , zasadniczo mam wybór między defaultfloat i fixed podczas formatowania liczb zmiennoprzecinkowych bez notacji wykładniczej (z liczbami dziesiętnymi).Jak sformatować wartość zmiennoprzecinkową tak, aby nigdy nie używała notacji wykładniczej ani zerowania końcowego?

Jednakże chcę wybrać maximum precision które produkują dużo zera kończące dla fixed dla wielu numerów (np 1.) ale uniknąć kiedykolwiek użyciem notacji wykładnik. Jeśli ustawione na defaultfloat, będzie wyglądało dobrze większość czasu, chyba że wartość jest naprawdę bardzo mała, ale nie 0.. W takim przypadku domyślna reprezentacja przełącza się na samą notację naukową, która przerywa odbiornik sformatowanego wyjścia (ponieważ nie ma pojęcia, co oznacza 2.22045e-16 oznacza:

Więc, jak mogę zjeść moje ciasto i je zjeść? ? to znaczy, non-wykładnik notacja bez zbędnych zer wahaczami wzdłużnymi


. Uwaga: nie testować działanie flaga defaultfloat, ponieważ mój gcc nie wydaje się, aby wdrożyć tę flagę (dotychczas), ale zakładam, że jest to ustawienie domyślne, które obowiązuje bez użycia flagi. Sprawdziłem numerFlaga, która działa zgodnie z oczekiwaniami.

+0

Co dokładnie chcesz wydrukować, gdy wyjście ma wartość 2.22045e-16? '0' lub' 0.00000000000000000222045' [nie wiem, czy mam odpowiednią liczbę zer, ale udawam, że jest ...] –

+0

@MatsPetersson: Drugi. – bitmask

+0

A jeśli wartość wynosi 16.00001210121019, chcesz exaxctly, ale jeśli wartość jest 16,5, chcesz to, mam rację? Myślę, że najlepiej będzie napisać własny "float to string". To nie jest strasznie trudne. –

Odpowiedz

1

Ponieważ nie wydaje się być odpowiedni sposób na zrobienie tego przy pomocy stdlib, oto moje opakowanie.

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

Niestety, te kody "." (Patrz http: //coliru.stacked-crooked .com/a/b72ae760073d8832) –

+0

Zdaję sobie sprawę, że zaakceptowałeś własną odpowiedź, więc ten kod działa dla ciebie, ale nie spełnia on wymogu "nigdy nie używa notacji wykładniczej" (spróbuj bardzo dużych lub bardzo małych liczb). Odpowiedź Matsa jest bliższa uderzenia w ten gwóźdź. Piszę ten komentarz, aby poinformować innych, którzy potrzebują funkcji bez wykładników. – slajoie

0

Nie ma wbudowanego formatowania, które zrobi to za Ciebie. Możesz albo stworzyć swój własny formator od zera, albo napisać taki, który przetwarza ciąg znaków, aby usunąć niepotrzebne zera końcowe.

Na przykład można to zrobić za pomocą regex podstawienie:

#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'; 
} 

Choć przy użyciu regex prawdopodobnie nie jest szczególnie efektywne rozwiązanie.

+0

Niestety, ten kod jest "." (Patrz http://coliru.stacked-crooked.com/a/b72ae760073d8832) –

3

Prosty sposób byłoby coś takiego:

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; 
} 

Dałem mu kilka testów. Największym problemem jest to, że daje ogromną liczbę miejsc dziesiętnych dla niektórych numerów, a takie rzeczy jak 0,05 wychodzi jako 0.05000000000000000277555756156289135105907917022705078125 i 0,7 postać: 0.05000000000000000277555756156289135105907917022705078125

To dlatego, że to nie jest „dokładny” liczba w postaci binarnej.

Myślę, że rozwiązaniem jest obliczenie w przybliżeniu liczby cyfr w wyniku, poprzez pobranie liczby cyfr całkowitych (lub piętra (log (f)/log (10) +1)), a następnie odjęcie tej liczby ze stałej, na przykład 17, czyli liczba cyfr, których można oczekiwać od podwójnego. Następnie usuń nadmiarowe zera.

Zostawię że w tej chwili, ponieważ mam kilka innych rzeczy do zrobienia, że ​​powinienem zacząć na jakiś czas temu ...;)

+0

Dzięki. +1 za wysiłek, ale [moja wersja] (http://stackoverflow.com/a/14966914/430766) wydaje się odpowiednio traktować takie liczby. – bitmask

+0

Niestety, to hardcodes '.' (patrz http://coliru.stacked-crooked.com/a/b72ae760073d8832) –

+0

Std :: naprawiony pracował dla moich potrzeb, dzięki. –

Powiązane problemy