9

ios_baseマニピュレーターによると、私は基本的に、指数表記なし(10進数付き)で浮動小数点数をフォーマットするか、フォーマット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)) を取り、その数値を定数から差し引いて、結果に必要な桁数を大まかに計算することだと思います。 double から期待できる桁数である 17 など。次に、余分なゼロを削除します。

少し前に始めるべきだった他のことをする必要があるので、それはしばらく置いておきます... ;)

于 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

これを行う組み込みの書式設定はありません。独自のフォーマッタを最初から作成するか、文字列を後処理して不要な末尾のゼロを削除するフォーマッタを作成することができます。

たとえば、正規表現の置換を使用して実行できます。

#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 に答える