[英]Match round behavior of std::stringstream with boost::spirit::karma::real_generator
我想將boost::spirit::karma::real_generator
的 output 與std::stringstream
的 output 匹配。
以下代碼使用以下代碼將雙8612.0078125
轉換為精度為 6 的std::string
:
stringstream
和std::setprecision
boost::spirit::karma::real_generator
但是,它們在最后一位的表現不同。 output:
8612.007812
8612.007813
如果有舍入行為的策略,我檢查了karma::real_policies
,但是在調用fraction_part
時n
已經是 7813。
#include <boost/spirit/include/karma.hpp>
#include <iomanip>
template <typename T>
struct precision_policy : boost::spirit::karma::real_policies<T>
{
int floatfield(T n) const { return boost::spirit::karma::real_policies<T>::fmtflags::fixed; } // Always Fixed
bool trailing_zeros(T n) const{ return true; }
precision_policy(int prec):precision_(prec){}
int precision(T n) const { return precision_; }
int precision_;
};
// https://stackoverflow.com/questions/42255919/using-boost-karma-to-replace-stdstringstream-for-double-to-stdstring-convers
std::string ToStringFixedKarma(double d, const unsigned int width = 6)
{
using boost::spirit::karma::real_generator;
using boost::spirit::ascii::space;
using boost::spirit::karma::generate;
real_generator<double,precision_policy<double> > my_double_(width);
std::string s;
std::back_insert_iterator<std::string> sink(s);
generate(sink, my_double_, d);
return s;
}
int main(){
double my_double = 8612.0078125;
std::stringstream description;
description << std::fixed << std::setprecision(6)
<< my_double << "\n";
std::cout << description.str();
std::cout << ToStringFixedKarma(my_double) << "\n";
}
我討厭成為那種人,但這強烈感覺像是方釘和圓孔的問題。
業力有它的用途,但孤立地格式化實數,我不認為它是不可避免的。
鏈接的答案表明您可能會追求性能提升。 我懷疑這也可以通過在同一個std::string
實例上重復使用 stream 來實現。 這是一個簡單的方法 - 顯然 - 復制了iostream
行為:
std::string ToStringFixedNoKarma(double d, const unsigned int width = 6) {
using D = boost::iostreams::back_insert_device<std::string>;
std::string s;
boost::iostreams::stream<D> ss(D{s});
ss << std::fixed << std::setprecision(width) << d;
return s;
}
由於能夠從字符串結果移動和/或重用支持字符串實例,您可以期望它更快。
其他想法/靈感來源:
Boost Convert 有一系列轉換方法,包括基准測試
還要注意關於 Karma 性能的評論。
如果您追求正確性和速度,請考慮Libfmt :
- 快速的 IEEE 754 浮點格式化程序,具有正確的舍入、縮短和往返保證
他們也發布基准:
C++17 to_chars
<charconv>
中有新的 to_chars。 可悲的是 GCC 還沒有完全實現它(但 MSVC 實現了)。
為了提供一點幫助,這里列出了許多提到的方法:
#include <boost/convert.hpp>
#include <boost/convert/lexical_cast.hpp>
#include <boost/convert/parameters.hpp>
#include <boost/convert/printf.hpp>
#include <boost/convert/stream.hpp>
#include <boost/convert/strtol.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <fmt/printf.h>
#include <charconv>
#include <iostream>
using Reference = boost::multiprecision::cpp_dec_float_100;
std::string ToStringFixedKarma(double d, const unsigned int width = 6) {
using D = boost::iostreams::back_insert_device<std::string>;
std::string s;
boost::iostreams::stream<D> ss(D{s});
ss << std::fixed << std::setprecision(width) << d;
return s;
}
void comparisons(std::string_view label, Reference value, auto converter) {
double d = value.convert_to<double>();
float f = value.convert_to<float>();
std::cout << " ---- " << label << "\n";
std::cout << "float" << converter(f) << "\n";
std::cout << "double" << converter(d) << "\n";
}
int main() {
namespace cnv = boost::cnv;
namespace arg = boost::cnv::parameter;
cnv::cstream _cs;
cnv::lexical_cast _lc; // not able to control format
cnv::strtol _stl;
cnv::printf _pf;
Reference const reference("8612.0078125");
std::cout << "Reference: " << reference.str(20, std::ios::fixed) << "\n";
_cs(std::fixed)(std::setprecision(6));
_stl(arg::notation = cnv::notation::fixed)(arg::precision = 6);
_pf(arg::notation = cnv::notation::fixed)(arg::precision = 6);
auto cs = cnv::apply<std::string>(boost::cref(_cs));
auto stl = cnv::apply<std::string>(boost::cref(_stl));
auto pf = cnv::apply<std::string>(boost::cref(_pf));
auto lc = cnv::apply<std::string>(boost::cref(_lc));
comparisons("cstream", reference, cs);
comparisons("strtol", reference, stl);
comparisons("printf", reference, pf);
comparisons("libfmt", reference, [](auto v) { return fmt::format(FMT_STRING("{:.10}"), v); });
#ifdef __cpp_lib_to_chars
comparisons("charconv", reference,
[buf = std::array<char, 30>{}](auto v) mutable {
auto r = std::to_chars(buf.data(), buf.data() + buf.size(),
v, std::chars_format::fixed, 6);
return std::string_view(buf.data(), r.ptr - buf.begin());
});
#endif
comparisons("lexical_cast", reference, lc);
}
印刷
Reference: 8612.00781250000000000000
---- cstream
float8612.007812
double8612.007812
---- strtol
float8612.007813
double8612.007813
---- printf
float8612.007812
double8612.007812
---- libfmt
float8612.007812
double8612.007812
---- lexical_cast
float8612.00781
double8612.0078125
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.