简体   繁体   中英

iomanip set at most 4 digits after decimal point

I want to print to screen some numbers with at most 4 digits after decimal point using iomanip .

I've learned that in default mode setprecision counts not only the digits after decimal point but also the digits in the integer part. This code

#include <iostream>
#include <iomanip>

int main () {
    double numbers[] = {3.141516, 1.01, 200.78901, 0.12345};
    int len = sizeof(numbers) / sizeof(numbers[0]);

    std::cout << std::setprecision(4);
    for (int i = 0; i < len; ++i) {
        std::cout << numbers[i] << '\n';
    }
    return 0;
}

outputs:

3.142
1.01
200.8
0.1235

But what I want is: (at most 4 digits after decimal point without trailing zeros)

3.1415
1.01
200.789
0.1235

Is iomanip capable of doing this? Without using other tricks (like round )?

EDIT

It seems that I haven't made it clear enough. My question is iomanip specific

All I want to know is whether iomanip is capable of doing things I've described because iomanip is said to be the standard library for input/output manipulators. The posted question is

Is iomanip capable of doing this?

It's more like "is it supported" rather than "give me any solution".

I have searched it again, looked up iomanip references hoping for a clean and compact way to format floating numbers for at most n digits, using unnecessary libraries as little as possible.

And there seems to be no standard way to achieve this.

One (ugly) option to obtain OP's desired output is to represent the number with the wanted maximum precision and then just remove the unwanted zeroes:

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>

int main()
{
    std::vector<double> numbers {
        3.141516, 1.01, 200.78901, 0.12345, 9.99999
    };

    for (auto x : numbers)
    {
        // "print" the number
        std::stringstream ss;
        ss << std::fixed << std::setprecision(4) << x;

        // remove the unwanted zeroes
        std::string result{ss.str()};
        while (result.back() == '0')
            result.pop_back();

        // remove the separator if needed   
        if (result.back() == '.')
            result.pop_back();

        std::cout << result  << '\n';
    }

    std::cout << "\nJustified:\n";
    for (auto x : numbers)
    {
        // "print" the number
        std::stringstream ss;
        ss << std::fixed << std::setprecision(4) << std::setw(15) << x;

        // remove the unwanted zeroes
        std::string result{ss.str()};
        auto it = result.rbegin();
        while (*it == '0')
            *it++ = ' ';

        // remove the separator if needed   
        if (*it == '.')
            *it = ' ';

        std::cout << result  << '\n';
    }
}

Live example: https://ideone.com/8zP17O

So:

  • in std::fixedfield mode, std::setprecision sets the maximum number of significant figures , not decimal places;
  • if you flip to std::fixed it means exact number of decimal places;
  • C++ does not provide any analogue to the %.Nf printf format string! (up to N decimal places)

I've just thrown together a little fake I/O manipulator that can get the behaviour we both want. It's not terribly performant, but it does the job:

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <cstdio>

struct sprintf_wrapper
{
    sprintf_wrapper(std::string fmt_str)
        : _fmt_str(std::move(fmt_str))
    {}

    struct proxy
    {
        proxy(const sprintf_wrapper& wrapper, std::ostream& os)
            : _wrapper(wrapper)
            , _os(os)
        {}

        std::ostream& Resolve(const double value) const
        {
            // First find out how many characters we're going to need
            const int len = std::snprintf(nullptr, 0, _wrapper._fmt_str.c_str(), value);
            if (len < 0)
            {
                _os.setstate(std::ios::failbit);
                return _os;
            }

            // Then allocate a buffer
            std::string result;
            result.resize(len);

            // Actually serialise the value according to the format string
            if (std::sprintf(result.data(), _wrapper._fmt_str.c_str(), value) < 0)
            {
                _os.setstate(std::ios::failbit);
                return _os;
            }

            // Stream it out
            _os.write(result.data(), result.size());
            return _os;
        }

        friend std::ostream& operator<<(const proxy& obj, const double val)
        {
            return obj.Resolve(val);
        }

    private:
        const sprintf_wrapper& _wrapper;
        std::ostream& _os;
    };

    friend proxy operator<<(std::ostream& os, const sprintf_wrapper& obj)
    {
        return proxy(obj, os);
    }

private:
    std::string _fmt_str;
};

inline auto sprintf_f(size_t n, const bool showpos = false)
{
    std::stringstream fmt;
    fmt << '%';
    if (showpos) fmt << '+';
    fmt << '.' << n << 'f';

    return sprintf_wrapper(fmt.str());
}

int main()
{
    std::cout << sprintf_f(2) << 4.123456789 << '\n';
    std::cout << sprintf_f(3) << 4.123456789 << '\n';
    std::cout << sprintf_f(4) << 4.123456789 << '\n';
    std::cout << sprintf_f(5) << 4.123456789 << '\n';
    std::cout << sprintf_f(6) << 4.123456789 << '\n';
}

( live demo )

Use a combination of std::fixed and std::setprecision .

#include <iomanip>
#include <iostream>

int main() {
    double d = 311.3456;
    std::cout << std::fixed;
    std::cout << std::setprecision(4);
    std::cout << d << std::endl;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM