简体   繁体   English

std::ostream 的浮点格式

[英]Floating point format for std::ostream

How do I do the following with std::cout?如何使用 std::cout 执行以下操作?

double my_double = 42.0;
char str[12];
printf_s("%11.6lf", my_double); // Prints " 42.000000"

I am just about ready to give up and use sprintf_s.我正准备放弃并使用 sprintf_s。

More generally, where can I find a reference on std::ostream formatting that lists everything in one place, rather than spreading it all out in a long tutorial?更一般地说,我在哪里可以找到关于 std::ostream 格式的参考,将所有内容都列在一个地方,而不是在一个长教程中全部展开?

EDIT Dec 21, 2017 - See my answer below.编辑 2017 年 12 月 21 日 - 请参阅下面的答案。 It uses features that were not available when I asked this question in 2012.它使用了我在 2012 年问这个问题时不可用的功能。

std::cout << std::fixed << std::setw( 11 ) << std::setprecision( 6 ) << my_double;

You need to add你需要添加

#include <iomanip>

You need stream manipulators你需要流操纵器

You may "fill" the empty places with whatever char you want.你可以用你想要的任何字符“填充”空的地方。 Like this:像这样:

std::cout << std::fixed << std::setw( 11 ) << std::setprecision( 6 ) 
          << std::setfill( '0' ) << my_double;
std::cout << boost::format("%11.6f") % my_double;

你必须#include <boost\\format.hpp>

In general, you want to avoid specifying things like 11 and 6 at the point of output.通常,您希望避免在输出点指定116之类的内容。 That's physical markup, and you want logical markup;那是物理标记,您需要逻辑标记; eg pressure , or volume .例如pressurevolume That way, you define in a single place how pressure or volume are formatted, and if that formatting changes, you don't have to search through out the program to find where to change the format (and accidentally change the format of something else).这样,您可以在一个地方定义压力或体积的格式,如果格式发生变化,您不必搜索整个程序以找到更改格式的位置(并且不小心更改了其他格式) . In C++, you do this by defining a manipulator, which sets the various formatting options, and preferrably restores them at the end of the full expression.在 C++ 中,您可以通过定义一个操纵器来实现这一点,该操纵器设置各种格式选项,并最好在完整表达式的末尾恢复它们。 So you end up writing things like:所以你最终会写出这样的东西:

std::cout << pressure << my_double;

Although I definitly wouldn't use it in production code, I've found the following FFmt formatter useful for quicky jobs:虽然我绝对不会在生产代码中使用它,但我发现以下FFmt格式化程序对于快速作业很有用:

class FFmt : public StateSavingManip
{
public:
    explicit            FFmt(
                            int                 width,
                            int                 prec = 6,
                            std::ios::fmtflags  additionalFlags 
                                    = static_cast<std::ios::fmtflags>(),
                            char                fill = ' ' );

protected:
    virtual void        setState( std::ios& targetStream ) const;

private:
    int                 myWidth;
    int                 myPrec;
    std::ios::fmtflags  myFlags;
    char                myFill;
};

FFmt::FFmt(
    int                 width,
    int                 prec,
    std::ios::fmtflags  additionalFlags,
    char                fill )
    :   myWidth( width )
    ,   myPrec( prec )
    ,   myFlags( additionalFlags )
    ,   myFill( fill )
{
    myFlags &= ~ std::ios::floatfield
    myFlags |= std::ios::fixed
    if ( isdigit( static_cast< unsigned char >( fill ) )
             && (myFlags & std::ios::adjustfield) == 0 ) {
        myFlags |= std::ios::internal
    }
}

void
FFmt::setState( 
    std::ios&           targetStream ) const
{
    targetStream.flags( myFlags )
    targetStream.width( myWidth )
    targetStream.precision( myPrec )
    targetStream.fill( myFill )
}

This allows writing things like:这允许编写如下内容:

std::cout << FFmt( 11, 6 ) << my_double;

And for the record:并且为了记录:

class StateSavingManip
{
public:
    StateSavingManip( 
            StateSavingManip const& other );
    virtual             ~StateSavingManip();
    void                operator()( std::ios& stream ) const;

protected:
    StateSavingManip();

private:
    virtual void        setState( std::ios& stream ) const = 0;

private:
    StateSavingManip&   operator=( StateSavingManip const& );

private:
    mutable std::ios*   myStream;
    mutable std::ios::fmtflags
                        mySavedFlags;
    mutable int         mySavedPrec;
    mutable char        mySavedFill;
};

inline std::ostream&
operator<<(
    std::ostream&       out,
    StateSavingManip const&
                        manip )
{
    manip( out );
    return out;
}

inline std::istream&
operator>>(
    std::istream&       in,
    StateSavingManip const&
                        manip )
{
    manip( in );
    return in;
}

StateSavingManip.cc: StateSavingManip.cc:

namespace {

//      We maintain the value returned by ios::xalloc() + 1, and not
//      the value itself.  The actual value may be zero, and we need
//      to be able to distinguish it from the 0 resulting from 0
//      initialization.  The function getXAlloc() returns this value
//      -1, so we add one in the initialization.
int                 getXAlloc();
int                 ourXAlloc = getXAlloc() + 1;

int
getXAlloc()
{
    if ( ourXAlloc == 0 ) {
        ourXAlloc = std::ios::xalloc() + 1;
        assert( ourXAlloc != 0 );
    }
    return ourXAlloc - 1;
}
}

StateSavingManip::StateSavingManip()
    :   myStream( NULL )
{
}

StateSavingManip::StateSavingManip(
    StateSavingManip const&
                        other )
{
    assert( other.myStream == NULL );
}

StateSavingManip::~StateSavingManip()
{
    if ( myStream != NULL ) {
        myStream->flags( mySavedFlags );
        myStream->precision( mySavedPrec );
        myStream->fill( mySavedFill );
        myStream->pword( getXAlloc() ) = NULL;
    }
}

void
StateSavingManip::operator()( 
    std::ios&           stream ) const
{
    void*&              backptr = stream.pword( getXAlloc() );
    if ( backptr == NULL ) {
        backptr      = const_cast< StateSavingManip* >( this );
        myStream     = &stream;
        mySavedFlags = stream.flags();
        mySavedPrec  = stream.precision();
        mySavedFill  = stream.fill();
    }
    setState( stream );
}

In C++20 you'll be able to do在 C++20 中,你将能够做到

double my_double = 42.0;
char str[12];
std::format_to_n(str, sizeof(str), "{:11.6}", my_double); 

or要么

std::string s = std::format("{:11.6}", my_double); 

In the meantime you can use the {fmt} library that provides an implementation of format_to_n .同时,您可以使用提供format_to_n实现的 {fmt} 库

Disclaimer : I'm the author of {fmt} and C++20 std::format .免责声明:我是 {fmt} 和 C++20 std::format

#include <iostream>
#include <iomanip>

int main() {
    double my_double = 42.0;
    std::cout << std::fixed << std::setw(11)
        << std::setprecision(6) << my_double << std::endl;
    return 0;
}

For future visitors who prefer actual printf-style format specs with std::ostream, here is yet another variation, based on Martin York's excellent post in another SO question: https://stackoverflow.com/a/535636 :对于喜欢使用 std::ostream 的实际 printf 样式格式规范的未来访问者,这里还有另一种变体,基于 Martin York 在另一个 SO 问题中的出色帖子: https : //stackoverflow.com/a/535636

#include <iostream>
#include <iomanip>
#include <stdio.h> //snprintf

class FMT
{
public:
    explicit FMT(const char* fmt): m_fmt(fmt) {}
private:
    class fmter //actual worker class
    {
    public:
        explicit fmter(std::ostream& strm, const FMT& fmt): m_strm(strm), m_fmt(fmt.m_fmt) {}
//output next object (any type) to stream:
        template<typename TYPE>
        std::ostream& operator<<(const TYPE& value)
        {
//            return m_strm << "FMT(" << m_fmt << "," << value << ")";
            char buf[40]; //enlarge as needed
            snprintf(buf, sizeof(buf), m_fmt, value);
            return m_strm << buf;
        }
    private:
        std::ostream& m_strm;
        const char* m_fmt;
    };
    const char* m_fmt; //save fmt string for inner class
//kludge: return derived stream to allow operator overloading:
    friend FMT::fmter operator<<(std::ostream& strm, const FMT& fmt)
    {
        return FMT::fmter(strm, fmt);
    }
};

usage example:用法示例:

double my_double = 42.0;
cout << FMT("%11.6f") << my_double << "more stuff\n";

or even:甚至:

int val = 42;
cout << val << " in hex is " << FMT(" 0x%x") << val << "\n";

it's me, the OP, Jive Dadson - five years on.是我,OP,Jive Dadson - 五年过去了。 C++17 is becoming a reality. C++17 正在成为现实。

The advent of variadic template parameters with perfect forwarding has made life so much simpler.具有完美转发的可变参数模板参数的出现让生活变得如此简单。 The chained madness of ostream<< and boost::format% can be dispensed with.可以免除 ostream<< 和 boost::format% 的连锁疯狂。 The function oprintf below fills the bill.下面的函数 oprintf 可以满足要求。 Work in progress.工作正在进行中。 Feel free to chime in on error-handling, etc...随意加入错误处理等...

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <string_view>

namespace dj {

    template<class Out, class... Args>
    Out& oprintf(Out &out, const std::string_view &fmt, Args&&... args) {
        const int sz = 512;
        char buffer[sz];
        int cx = snprintf(buffer, sz, fmt.data(), std::forward<Args>(args)...);

        if (cx >= 0 && cx < sz) { 
            return out.write(buffer, cx);
        } else if (cx > 0) {
            // Big output
            std::string buff2;
            buff2.resize(cx + 1);
            snprintf(buff2.data(), cx, fmt.data(), std::forward<Args>(args)...);
            return out.write(buff2.data(), cx);
        } else {
            // Throw?
            return out;
        }
    }
}

int main() {
    const double my_double = 42.0;
    dj::oprintf(std::cout, "%s %11.6lf\n", "My double ", my_double);
    return 0;
}

Some great answers already;已经有一些很棒的答案; kudos to those!为那些点赞!

This is based on some of them.这是基于其中的一些。 I have added type assertions for POD types, since they are the only safe types usable with printf() .我为 POD 类型添加了类型断言,因为它们是printf()唯一可用的安全类型。

#include <iostream>
#include <stdio.h>
#include <type_traits>

namespace fmt {
namespace detail {

template<typename T>
struct printf_impl
{
    const char* fmt;
    const T v;

    printf_impl(const char* fmt, const T& v) : fmt(fmt), v(v) {}
};

template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, std::ostream& >::type
operator<<(std::ostream& os, const printf_impl<T>& p)
{
    char buf[40];
    ::snprintf(buf, sizeof(buf), p.fmt, p.v, 40);
    return os << buf;
}

} // namespace detail

template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, detail::printf_impl<T> >::type
printf(const char* fmt, const T& v)
{
    return detail::printf_impl<T>(fmt, v);
}

} // namespace fmt

Example usage it as below.示例用法如下。

std::cout << fmt::printf("%11.6f", my_double);

Give it a try on Coliru .试试 Coliru 吧

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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