Formatowanie wyświetlanych danych.

0

Chciałbym zapisywać dane do pliku. Doczytałem o używaniu fixed, precision, showpoint i innych manipulatorach. Zapisywane dane to double.

Przykładowy prosty program który jednak nie formatuje danych w wymagany sposób:

#include <iostream>
#include <iomanip>      // std::setprecision
using namespace std;

int main()
{
	cout << setprecision(4) << fixed;
	double liczba1 = 123.123456789;
	std::cout << "Wynik " << liczba1 << std::endl;
	double liczba2 = 123.110;
	std::cout << "Wynik " << liczba2 << std::endl;
	double liczba3 = 123.0;
	std::cout << "Wynik " << liczba3 << std::endl;
    return 0;
}

Wyniki są takie:
Wynik 123.1235
Wynik 123.1100
Wynik 123.0000

Chciałbym żeby dane były w takiej formie (bez nieznaczących zer):
Wynik 123.1235
Wynik 123.11
Wynik 123.

Kropka w ostatnim wyniku 123. <- jest ważna (potrzebuje ją inny system)

Czy mogę to osiągnąć przy użyciu standardowych manipulatorów czy trzeba będzie napisać jakiś własny?

2

Możesz do tego celu użyć funkcji format.

#include <iostream>
#include <iomanip>

using namespace std;

string format( double number , int precision_ = 10 )
{
   ostringstream out;
   out.precision(precision_);
   out << std::fixed << number;
   string result {out.str()};
   result.erase( result.find_last_not_of('0') + 1, string::npos );
   return result;
}

int main()
{   
    double liczba1 = 123.123456789;
    cout << "Wynik " << format(liczba1) << endl;
    double liczba2 = 123.110;

    cout << "Wynik " << format(liczba2) << endl;
    double liczba3 = 123.0;
    cout << "Wynik " << format(liczba3) << endl;
    return 0;

    return 0;
}
0

Dziękuję bardzo za inspirację.
Szukałem rozwiązania w złym miejscu, bo myślałem że w jakiś sposób da się przestawić to formatowanie.

Może komuś się przyda. Zrobiłem manipulator.

#include <iostream>
#include <iomanip>

using namespace std;

class toogle_t
{};
constexpr toogle_t formatuj;
struct toogled_ostream {
	std::ostream& os;
};

inline toogled_ostream operator << (std::ostream& os, toogle_t) {
	return { os };
}

template <typename T>
std::ostream& operator << (toogled_ostream tos, const T& v) {
	return tos.os << v;
}
std::ostream& operator << (toogled_ostream tos, double v) {
		int precision_ = 6 ;
	   ostringstream out;
	   out.precision(precision_);
	   out << std::fixed << v;
	   string result = out.str();
	   result.erase( result.find_last_not_of('0') + 1, string::npos );
	   return tos.os <<  result;
}

int main() {
	double liczba1 = 123.002000;
	std::cout << formatuj << liczba1 << endl;

	double liczba2 = 0.00000;
	std::cout << formatuj << liczba2 << endl;

	return 0;
}

To otrzymuje na wyjściu:
123.002
0.

0
idepozapalki napisał(a):

Kropka w ostatnim wyniku 123. <- jest ważna (potrzebuje ją inny system)

Jakoś słabo mi się wierzy że system który przyjmuje:
123.
nie przyjmie:
123.000000

0

Oczywiście że przyjmuje.
Chodzi o to, że różne są skutki działania gdy kropka jest i gdy jej nie ma.
Po prostu kropka jest informacją jak ma być interpretowana otrzymana cyfra - to tak dodatkowa informacja.

1

Ja bym się zainteresował czy można to załatwić w jakiś bardziej czysty sposób, używając std::locale.

#include <iostream>
#include <sstream>
#include <iomanip>
#include <iterator>
#include <locale>
#include <algorithm>

struct trim_tailig_zeros : std::num_put<char> {
    using Super = std::num_put<char>;
    iter_type do_put(iter_type out, 
                     std::ios_base& str, 
                     char_type fill, 
                     double v) const override
    {
        std::stringbuf buff;
        auto r = Super::do_put(
            std::ostreambuf_iterator<char>{&buff}, 
            str, fill, v);

        auto s = buff.str();
        auto end = std::find_if_not(s.rbegin(), s.rend(),
                                    [](auto ch) { return ch == '0'; }).base();


        return std::copy(s.begin(), end, out);
    }
};

int main()
{
    std::cout.imbue(std::locale(std::cout.getloc(), new trim_tailig_zeros));
    std::cout << std::showpoint << std::setprecision(8);
    
    double x;
    while(std::cin >> x) {
        std::cout << "Wynik " <<  x << std::endl;
    }
    return 0;
}

Demo.

Oczywiście to jest bardzo niedopracowane. Jest sporo warunków brzegowych do uwzględnienia.
Np. wielkość pola, potencjalna eksponenta.
A co najważniejsze co jeśli nie ma kropki (w przykładzie zawsze jest, bo ją wymuszam)?

1

Teraz nie dziwi mnie dlaczego C++ nie odniósł sukcesu (a w szczególności iostream):

// JS: toString, add '.' if the number does not contain it
(123.030).toString().replace(/^(\d+)$/, '$1.')

// same in Java
Double.toString(32300).replaceAll("^(\\d+)$", "\1.").replaceAll("0+$", "")

// And C# version
(332.09800).ToString(".000000000000000000").TrimEnd('0')

:P

0
idepozapalki napisał(a):

Oczywiście że przyjmuje.
Chodzi o to, że różne są skutki działania gdy kropka jest i gdy jej nie ma.
Po prostu kropka jest informacją jak ma być interpretowana otrzymana cyfra - to tak dodatkowa informacja.

Wyjaśnię to obrazowo żeby nie było niejasności.
Cyfry to informacja o przesunięciu czegoś.
System dla którego przeznaczone są dane działa w taki sposób, że jeżeli dostanie cyfrę z kropką to przesuwa przedmiot o wartość do kropki (w jednostkach innych niż bez kropki).
Przykładowo te 123. - to przesunięcie o 123cm
Bez kropki system czyta przemieszczenie w milimetrach, więc 123 to dla niego przesunięcie o 123milimetry.
Chyba widać teraz, że tu kropka ma znaczenie i to zasadnicze.
Tak to działa i nie będzie zmian w działaniu więc konieczne jest rozważne operowanie kropką.

0

Jak w programie rozpoznajesz co jest milimetrami a co centymetrami, skoro wszystko jest po prostu doublem?

1 użytkowników online, w tym zalogowanych: 0, gości: 1