2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C で浮動小数点数を正確に printf(文字列化) するライブラリのメモ

Last updated at Posted at 2020-05-22

背景

  • 浮動小数点数データを printf とかでファイルに ASCII 保存し, 再度読み込むと完全に同じ値になるとは限らない
  • レイトレーシングやレンダリングなど精度にシビアなアプリで, しかし他とのソフトウェア互換性のためなどで ASCII で数値を扱う必要がある

ライブラリ

とりあえずは Ryu を使っておけば問題なさそうです.

Converts floating point numbers to decimal strings
https://github.com/ulfjack/ryu

理論などは, ryu のページに paper があります.

double 限定ですと,

も使えそうでしょうか.

References

Reducing {fmt} library size 4x using Bloaty McBloatface
http://www.zverovich.net/2020/05/21/reducing-library-size.html

からの情報.

Grisu3
https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf

Dragon4
http://kurtstephens.com/files/p372-steele.pdf

(Ryu(龍) はこの Dragon4 を参考にしたからですかね)

StaticJSON と組み合わせ, JSON 文字列として浮動小数点数を出力する

JSON で数値をテキスト出力したいが, 正確に print したいときがありますね.

StaticJSON と組み合わせてみます.

StaticJSON では, Convert で from_shadow, to_shadow を定義することでカスタムの変換をかけることができます.

from_shadow, to_shadow は primitive type(i.e. float, double)で定義しても使われないので, 明示的に変換したい場合はなにかしらクラスを定義します.

ここでは, Bounds2f(4 floats)クラスのサンプルを提示します.

namespace {

// Exactly serialize IEEE754 float value to decimal string using ryu library.
std::string float_to_string(const float &f) {
  char *s = f2s(f);
  std::cout << "to_shadow " << f << ", to_string = " << s << "\n";

  std::string ret = std::string(s);

  free(s);

  return ret;
}

} // namespace

namespace staticjson
{
template <>
struct Converter<Bounds2f>
{
    typedef std::array<std::string, 4> shadow_type;

    static std::unique_ptr<ErrorBase> from_shadow(const shadow_type& shadow, Bounds2f& value)
    {
      std::array<float, 4> results;
      for (size_t i = 0; i < 4; i++) {
        double result;
        Status status = s2d(shadow[i].c_str(), &result);
        if (status == SUCCESS) {
          // ok
        } else if (status == INPUT_TOO_SHORT) {
          return std::make_unique<error::CustomError>("Input string is too short");
        } else if (status == INPUT_TOO_LONG) {
          return std::make_unique<error::CustomError>("Input string is too long");
        } else {
          // TODO(LTE): Allow Infinity, NaN
          return std::make_unique<error::CustomError>("Malformed input");
        }

        results[i] = static_cast<float>(result);
      }

      value.p1.x = results[0];
      value.p1.y = results[1];
      value.p2.x = results[2];
      value.p2.y = results[3];

      return nullptr;
    }

    static void to_shadow(const Bounds2f& value, shadow_type& shadow)
    {
      shadow[0] = float_to_string(value.p1.x);
      shadow[1] = float_to_string(value.p1.y);
      shadow[2] = float_to_string(value.p2.x);
      shadow[3] = float_to_string(value.p2.y);
    }
};

} // namespace staticjson

  Bounds2f bbox;
  bbox.p1.x = 0.133f;
  bbox.p1.y = 0.1133f;
  bbox.p2.x = 2.3f; //std::numeric_limits<float>::infinity();
  bbox.p2.y = 4.0f; //std::numeric_limits<float>::quiet_NaN();
  std::string a = staticjson::to_json_string(bbox);
  std::cout << "json = " << a << std::endl;

  Bounds2f br;
  staticjson::ParseStatus err;
  bool ret = staticjson::from_json_string(a.c_str(), &br, &err);
  if (!ret) {
    std::cout << err.description() << "\n";
  }

  std::cout << "bbox.p1.x = " << br.p1.x << std::endl;
  std::cout << "bbox.p1.y = " << br.p1.y << std::endl;
  std::cout << "bbox.p2.x = " << br.p2.x << std::endl;
  std::cout << "bbox.p2.y = " << br.p2.y << std::endl;

json = ["1.33E-1","1.133E-1","2.3E0","4E0"]

bbox.p1.x = 0.133
bbox.p1.y = 0.1133
bbox.p2.x = 2.3
bbox.p2.y = 4

のようになります!(本来は, ビットが変わっていないかチェックしましょう!)

inf, nan は Infinity, NaN と print される. ryu_parse(s2d)で読むと MALFORMED_INPUT と解釈されるので注意です!

その他

llvm の APFloat も正確に扱えたような?

(少なくとも, 10 年くらいまえの知識ですが, ASCII IR で浮動小数点数を扱うときは exact に表現しないとエラーになり, そのエラーチェックなどに APFloat が使われていたような記憶が)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?