floats mit std::ostream ganz normal formatieren
-
Ich versuche gerade Fließkommazahlen mit
ostreamohne wissenschaftliche Notation zu formatieren und ohne unnötige Nullen am Ende. Klingt komisch, ist aber so.#include <sstream> #include <iostream> namespace { template <class Float> std::string float_to_sane_string(Float value) { std::ostringstream buffer; //gesucht: Optionen für vernünftige Formatierung buffer << value; return buffer.str(); } void check_sanity(double value, std::string const &expected) { std::string const formatted = float_to_sane_string(value); if (formatted != expected) { std::cerr << "Expected " << expected << ", got " << formatted << '\n'; } } } int main() { check_sanity(0.0, "0"); check_sanity(0.5, "0.5"); check_sanity(0.00005, "0.00005"); check_sanity(1000000000.0, "1000000000"); }Expected 0.00005, got 5e-05 Expected 1000000000, got 1e+09fixeddeaktiviert zwar die wissenschaftliche Notation, produziert aber zu viele Nullen am Ende:buffer << std::fixed << value;Expected 0, got 0.000000 Expected 0.5, got 0.500000 Expected 0.00005, got 0.000050 Expected 1000000000, got 1000000000.000000Weiß jemand wie das geht, was ich will?
-
Ich verstehe nicht, wieso du die iostreams verwenden willst.
-
template <class Float> std::string float_to_sane_string(Float value) { auto str = std::to_string(value); boost::algorithm::trim_right_if( str, [](char c){ return c == '0'; } ); return str; }Darauf hättest du aber auch locker selber kommen können.

Edit:
to_stringgeneriert übrigens nie die Exponentialnotation, weilto_stringmit einer Fließkommazahl als Argumentsprintfmit conversion-specifierfbzw.Lfaufruft, welche nur Dezimalnotation generieren.
Ob man das Anhängen der Nullen auch in den C++ IOStreams unterbinden kann, weiß ich nicht. Ist das in irgendeiner Weise performance-kritisch? Trimmen ist doch fix genug und leicht lesbar?
-
unverständlich schrieb:
Ich verstehe nicht, wieso du die iostreams verwenden willst.
Dann zeig mal, wie du das mit printf hin bekommst. Da geht das nämlich genau so wenig.
In dem Satz ist übrigens auch die Antwort für den Threadersteller versteckt: Es geht nicht. Zumindest nicht mit den Standardflags. Die kanonischen Wege, dies zu erreichen sind:
-Stringmanipulation: Erst lang in einen String ausgeben, dann die Nullen abschneiden. Das kann man auch schick als Funktion schreiben, so dass man etwas wiecout << my_format(value)schreiben kann.
-Tief in die IOStreams eintauchen, und sich eine passende num_put-Facet schreiben.
-
Hab das just 4 fun mal händisch gemacht:
#include <sstream> #include <iostream> #include <cassert> #include <cmath> namespace { template <class Float> std::string float_to_sane_string(Float value) { typedef unsigned long long Int; std::ostringstream buffer; if(value < 0) buffer << '-'; value = std::fabs(value); buffer << Int(value); value -= Int(value); assert(value < 1); if(value == 0) return buffer.str(); buffer << '.'; do { value *= 10; Float newValue = value - unsigned(value); if(newValue > 0.99) { buffer << (unsigned(value) + 1); break; } buffer << unsigned(value); value = newValue; assert(value < 1); } while(value != 0); return buffer.str(); } void check_sanity(double value, std::string const &expected) { std::string const formatted = float_to_sane_string(value); if (formatted != expected) { std::cerr << "Expected " << expected << ", got " << formatted << '\n'; } } } int main() { check_sanity(0.0, "0"); check_sanity(0.5, "0.5"); check_sanity(0.00005, "0.00005"); check_sanity(1000000000.0, "1000000000"); }IEEE-754 Geeks: Irgendetwas daran falsch mit der Ausnahme nan etc?
Ergebnisse sehen recht brauchbar aus bei meinen Tests.
-
Arcoth schrieb:
template <class Float> std::string float_to_sane_string(Float value) { auto str = std::to_string(value); boost::algorithm::trim_right_if( str, [](char c){ return c == '0'; } ); return str; }Darauf hättest du aber auch locker selber kommen können.

Edit:
to_stringgeneriert übrigens nie die Exponentialnotation, weilto_stringmit einer Fließkommazahl als Argumentsprintfmit conversion-specifierfbzw.Lfaufruft, welche nur Dezimalnotation generieren.
Ob man das Anhängen der Nullen auch in den C++ IOStreams unterbinden kann, weiß ich nicht. Ist das in irgendeiner Weise performance-kritisch? Trimmen ist doch fix genug und leicht lesbar?Das kommt nicht durch seine Tests da er ja erwartet dass 5.0 zu 5 wird, deine Lösung macht 5. daraus.
-
Das kommt nicht durch seine Tests da er ja erwartet dass 5.0 zu 5 wird, deine Lösung macht 5. daraus.
Das macht es kein bisschen komplizierter
template <class Float> std::string float_to_sane_string(Float value) { auto str = std::to_string(value); boost::algorithm::trim_right_if( str, [](char c){ return c == '0'; } ); if( !str.empty() && str.back() == '.' ) str.pop_back(); return str; }
-
Na klar, wollte es nur anmerken da er ja freundlicherweise sogar Testfälle geliefert hat.

-

-
SeppJ schrieb:
Es geht nicht. Zumindest nicht mit den Standardflags. Die kanonischen Wege, dies zu erreichen sind:
-Stringmanipulation: Erst lang in einen String ausgeben, dann die Nullen abschneiden. Das kann man auch schick als Funktion schreiben, so dass man etwas wiecout << my_format(value)schreiben kann.Danke für die Auskunft. Ich mache das jetzt mit einem lokalen
ostringstreamund Abschneiden.
Das muss auch gar nicht schnell sein. Ich wollte nur die Float-Formatierung nicht neu erfinden.