标准::ostream 的浮点格式
Floating point format for std::ostream
如何使用std::cout执行以下操作?
double my_double = 42.0;
char str[12];
printf_s("%11.6lf", my_double); // Prints " 42.000000"
我几乎准备放弃并使用sprintf_s。
更一般地说,我在哪里可以找到有关 std::ostream 格式的参考,该参考将所有内容列在一个地方,而不是在长教程中全部展开?
编辑 Dec 21, 2017 - 请参阅下面的答案。它使用了我在 2012 年提出这个问题时不可用的功能。
std::cout << std::fixed << std::setw(11) << std::setprecision(6) << my_double;
您需要添加
#include <iomanip>
您需要流操纵器
你可以用任何你想要的字符"填充"空白的地方。喜欢这个:
std::cout << std::fixed << std::setw(11) << std::setprecision(6)
<< std::setfill('0') << my_double;
std::cout << boost::format("%11.6f") % my_double;
你必须#include <boostformat.hpp>
在 C++20 中你可以做
double my_double = 42.0;
char str[12];
std::format_to_n(str, sizeof(str), "{:11.6}", my_double);
或
std::string s = std::format("{:11.6}", my_double);
在 C++20 之前,您可以使用提供 format_to_n
实现的 {fmt} 库。
免责声明:我是 {fmt} 和 C++20 std::format
的作者。
通常,您希望避免6
11
在输出点。 这是物理标记,您需要逻辑标记;例如 pressure
,或volume
。 这样,您就可以在一个地方定义压力或体积的格式如何,如果格式发生变化,您不必在整个程序中搜索即可找到要更改的位置格式(并意外更改了其他内容的格式)。 在C++,您可以通过定义一个操纵器来执行此操作,该操纵器设置各种格式化选项,最好在完整版结束时恢复它们表达。 所以你最终会写这样的东西:
std::cout << pressure << my_double;
虽然我肯定不会在生产代码中使用它,但我发现以下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 )
}
这允许编写以下内容:
std::cout << FFmt( 11, 6 ) << my_double;
并记录在案:
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:
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 );
}
#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;
}
对于喜欢使用 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);
}
};
使用示例:
double my_double = 42.0;
cout << FMT("%11.6f") << my_double << "more stuffn";
甚至:
int val = 42;
cout << val << " in hex is " << FMT(" 0x%x") << val << "n";
是我,OP,Jive Dadson——五年过去了。C++17正在成为现实。
具有完美转发功能的可变参数模板参数的出现使生活变得更加简单。ostream<<和boost::format%的连锁疯狂可以省略。下面的函数 oprintf 填补了账单。工作正在进行中。随意插话错误处理等...
#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.6lfn", "My double ", my_double);
return 0;
}
已经有一些很好的答案了;向那些人致敬!
这是基于其中一些。我为 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
示例用法如下。
std::cout << fmt::printf("%11.6f", my_double);
在科利鲁身上试一试。
- 如何在openssl-ecc中获取十六进制格式的私钥
- 将"打开的CV图像"中的"颜色"转换为整数格式
- ostream过载时的缓冲区冲洗
- TDateTime格式在C++Builder中不会更改
- 需要从 istream 和 ostream 派生 iostream
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 检查不带转换的扫描格式
- 当我尝试使用 sstream 和分面将 Boost Time_duration转换为字符串时,我没有得到所需的格式
- 是否可以从格式字符串中检索"width"
- clang格式:宏的缩进
- clang格式:禁用排序包含
- 在用于格式4的arm模拟器中实现功能时的一个问题
- 将RGB图像保存为PPM格式
- 询问在设计我的手臂模拟器功能表示格式1
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 带有Protobuf序列化的C++Hazelcast:字符串不是UTF-8格式的
- 如何将strftime中的格式错误作为异常捕获
- 将CHW格式的浮点向量转换为cv::Mat
- 标准::ostream 的浮点格式
- C++在带有右值缓冲区的ostream中使用snprintf,这是格式正确的