簡體   English   中英

將 std::stringstream 的回合行為與 boost::spirit::karma::real_generator 匹配

[英]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

  1. stringstreamstd::setprecision
  2. boost::spirit::karma::real_generator

但是,它們在最后一位的表現不同。 output:

8612.007812
8612.007813

如果有舍入行為的策略,我檢查了karma::real_policies ,但是在調用fraction_partn已經是 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 實現了)。

為了提供一點幫助,這里列出了許多提到的方法:

Live On 編譯器資源管理器

#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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM