Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Global formatting options for floating point numbers

Tags:

c++

c++11

I know it is not possible to overload (stream) operators for primitive types as follows:

std::ostream & operator<<(std::ostream &stream, const double s) {
    stream << std::scientific << std::showpos << std::setprecision(15) << std::to_string(s);
    return stream;
}

What is the preferred way for defining global formatting options for primitive types? Note that I want to apply the formatting to any kind of output stream, not only specific streams like std::cout. C++11 solutions are welcome. Any hints?

like image 836
Marcel Avatar asked Oct 05 '15 09:10

Marcel


3 Answers

You could define your own manipulator for setting up the stream formatters. Your manipulator must conform to the format expected by the << operator:

basic_ostream& operator<<(
    std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );

So for instance:

template <class Char>
basic_ostream<Char>& format_double(basic_ostream<Char>& stream) {
    return stream << std::scientific 
                  << std::showpos 
                  << std::setprecision(15);
}

Then just do:

cout << format_double << 2.0;

References

  • << operator specially case (9)

Notes

There are two format for manipulators, one takes a ios_base as argument and the other a basic_ostream. You must use the later because it is not specified what is the return value of setprecision(int). For instance, my compiler (gcc-4.9) returns a struct _Setprecision holding the integer and defines a specific operator << for this struct and basic_ostream.

like image 70
fjardon Avatar answered Nov 14 '22 22:11

fjardon


Overloading streams is a bad idea, but you can wrap them. If you want to do a special but common pre/post-processing, define a custom (templated) class that has a stream as member (maybe affected in constructor), and delegates actual io to that stream after preprocessing input and/or do some post-processing after.

class Printer {
    ostream &ost;

    Printer(ostream& st): ost(st) {
        // other initializations ...
    }

    Printer& operator << (double d) {
        ost << std::scientific << std::showpos << std::setprecision(15) << std::to_string(s);
        return *this;
    }
    // others: ostream conversion, other processing...
}
like image 26
Serge Ballesta Avatar answered Nov 14 '22 21:11

Serge Ballesta


The formatting of the numeric types (integers and floating point numbers) can be modified by making sure streams are using a different std::locale. This facility can be used to effect the formatting of all streams which are created after the global std::locale is set. Existing streams can also be made to use this modified std::locale by imbue()ing.

An example could look something like this:

class special_num_put
    : public std::num_put<char> {
    virtual iter_type do_put(iter_type it, ios_base& fmt,
                             char_type fill, double v) const {
        fmt.precision(15);
        fmt.setf(std::ios_base::showpos);
        fmt.setf(std::ios_base::scientific, std::ios_base::floatfield);
        return this->std::num_put<char>::do_put(it, fmt, fill, v);
    }
    // likewise for all of the other numeric types
};

int main() {
    std::locale::global(std::locale(std::locale(), new special_num_put));
    std::cout.imbue(std::locale()); // deal with existing stream
    // likewise for all other preexisting streams
}

Note that I agree with the comment made earlier that you should not want to do anything like that.

like image 20
Dietmar Kühl Avatar answered Nov 14 '22 23:11

Dietmar Kühl