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

## 背景

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

## ライブラリ

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

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

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

double 限定ですと, 

https://github.com/google/double-conversion

も使えそうでしょうか.

### 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 と組み合わせてみます.

https://github.com/netheril96/StaticJSON

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

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

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

```c++
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

```

```c++
  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;
```

```c++

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 も正確に扱えたような?

https://llvm.org/doxygen/APFloat_8h_source.html

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