9

根据ios_base manipulators,我基本上可以在格式化没有指数表示法的浮点数(使用十进制数)之间defaultfloat和何时进行选择。fixed

但是,我想选择最大精度,这将为许多数字(例如)产生很多尾随零,但避免使用指数表示法。如果设置为,它会在大多数情况下看起来正确,除非该值真的很小,但不是。在这种情况下,默认表示会自行切换为科学记数法,这会破坏格式化输出的接收器(因为它不知道什么意思。fixed1.defaultfloat0.2.22045e-16

那么,我怎样才能拥有我的馅饼并吃掉它呢?也就是说,没有不必要的尾随零的非指数符号


注意:我没有测试该defaultfloat标志的效果,因为我的 gcc 似乎还没有实现该标志,但我认为它是默认设置,无需使用任何标志即可应用。我确实检查了fixed标志,它的行为符合预期。

4

4 回答 4

3

一个简单的方法是这样的:

std::string float2string(double f)
{
    std::stringstream ss;

    ss << std::fixed << std::setprecision(122) << f;   // 122 is LARGE, but you may find that really tiny or really large numbers still don't work out... 

    std::string s = ss.str();

    std::string::size_type len = s.length();

    int zeros = 0;
    while(len > 1 && s[--len] == '0')
        zeros++;
    if (s[len] == '.')  // remove final '.' if number ends with '.'
        zeros++;
    s.resize(s.length()-zeros);


    return s;
}

我已经对其进行了一些测试。最大的问题是它为某些数字提供了大量的小数,像 0.05 这样的东西出来,0.05000000000000000277555756156289135105907917022705078125 而 0.7 变成: 0.05000000000000000277555756156289135105907917022705078125

那是因为它不是二进制形式的“精确”数字。

我认为解决方案是通过取整数位数(或 floor(log(f)/log(10)+1))然后从常数中减去该数字来粗略计算您想要的结果位数,例如 17,这是您可以从 double 中获得的位数。然后删除多余的零。

我暂时先把它留下,因为我还有一些其他的事情要做,我应该在不久前就开始做……;)

于 2013-02-19T20:13:43.780 回答
1

由于似乎没有使用标准库执行此操作的正确方法,因此这是我的包装器。

  template <typename T>
  struct FpFormat {
    template <typename Stream>
    static Stream& setfmt(Stream& str) {
      return str;
    }
    template <typename String>
    static String const& untrail(String const& str) {
      return str;
    }
  };
  template <typename T>
  struct FpFormatFloats {
    template <typename Stream>
    static auto setfmt(Stream& str) -> decltype(str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10)) {
      return str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10);
    }
    template <typename String>
    static String untrail(String str) {
      if (str.find('.') == String::npos)
        return str;
      return ([](String s){
        return String(s.begin(),s.begin()+((s.back() == '.')?(s.size()-1):s.size()));
      })(str.substr(0,(str+"0").find_last_not_of('0')+1));
    }
  };
  template <> struct FpFormat<float> : FpFormatFloats<float> {};
  template <> struct FpFormat<double> : FpFormatFloats<double> {};
  template <> struct FpFormat<long double> : FpFormatFloats<long double> {};

  template <typename T>
  std::string toString(T x) {
    std::stringstream str;
    FpFormat<T>::setfmt(str) << x;
    return FpFormat<T>::untrail(str.str());
  }
于 2013-02-19T20:37:42.360 回答
1

iostream可悲的是,在或中似乎没有代码可以做到这一点printf()。任何“这个”我的意思是:

  1. 以固定格式打印。
  2. 打印最小位数,以便编码无损(即最接近的有效浮点数是您打印的那个)。

不能仅以非常大的精度打印并去除尾随零,因为可能存在不必要的非零数字。

我相信真正做到这一点的唯一好方法是使用像Ryu这样的专用代码。

另一种方法是以非常大的精度打印下一个和上一个数字,并取小数点后相同的数字(加一个)。不过会很丑。

于 2018-11-06T11:23:34.927 回答
-1

没有内置格式可以为您执行此操作。您可以从头开始生成自己的格式化程序,也可以编写一个对字符串进行后处理以删除不必要的尾随零的格式化程序。

例如,您可以使用正则表达式替换:

#include <iomanip>
#include <iostream>
#include <sstream>
#include <regex>

// not thoroughly tested
std::string format(double d, int precision = 100) {
   std::stringstream ss;
   ss << std::fixed << std::setprecision(precision);
   ss << d;

   return std::regex_replace(
       std::regex_replace(ss.str(), std::regex(R"((-?\d*\.\d*?)0*$)"), "$1"),
       std::regex(R"(\.$)"), "");
}

int main() {
   std::cout << format(-1) << '\n';
   std::cout << format(1e20) << '\n';
   std::cout << format(1e-20) << '\n';
   std::cout << format(2.22045e-16) << '\n';
}

尽管使用正则表达式可能不是一个特别有效的解决方案。

于 2013-02-19T20:04:58.637 回答