quadmath Perlでハマったところ備忘録
細々とメンテナンスしている Perlモジュールで quadmathが有効な Perlで失敗しているケースがいくつか見られたので対応していた. その対応に関する記録.
usequadmathとは
https://perldoc.perl.org/Config#usequadmath
数値の保持に可能であれば 4倍精度浮動小数点型である __float128を利用するコンフィグオプション. 設定を有効にしてビルドするためには libquadmathがインストールしておく必要がある.
名前だけ聞くと浮動小数点だけ影響があるのかと思うのだけど Perlような言語では整数とか浮動小数点はあいまいなので整数にも影響が出ておりテストがこけていた.
ハマった点
整数の精度が変わる
大きい整数リテラルを文字列化したときの精度が異なっており予期せぬテスト結果となっていた
#!/usr/bin/env perl print "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=", 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "\n"; print "0x800000000000000000000000000000=", 0x800000000000000000000000000000, "\n";
上記のスクリプトを quadmathが有効な Perlとそうでない Perlで実行した結果は以下の通り異なるものとなる.
quadmathが有効
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=8.30767497365572420564879412675215e+34 0x800000000000000000000000000000=6.64613997892457936451903530140172e+35
quadmathが無効
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=8.30767497365572e+34 0x800000000000000000000000000000=6.64613997892458e+35
対応は管理の保守の問題などから quadmath Perlではテストを行わないことにした.
数値から文字列への変換が適切に行えない
https://github.com/gfx/Perl-Data-Util/blob/f8db84cf6fe5c526adfa76d047bfd715296477bf/t/18_is_value.t
の is_integerの浮動小数点数の場合は数値を文字列化してそれを解析して判定を行っているが, 変換に使われていた関数が snprintfだったため正しく変換ができていなかった. snprintf等の標準ライブラリの変換関数は __float128には対応しておらず, quadmath_snprintfを使わなくてはならない.
https://perldoc.perl.org/perlapi#my_snprintf
ifdefで分けることを考えたが Perlのソースコードを眺めていたところ, my_snprintfが quadmathを有効にしてビルドした場合内部で quadmath_snprintfを呼び出すような実装になっていたのでそちらを呼ぶように切り替えた。
modifierと specifierはべた書きしてしまうとポータビリティがなくなるので, Perlで定義されているマクロを使っています.
おわりに
Perlみたいな動的型言語で標準で使われる浮動小数点数の精度を変えるというのは影響は多すぎると思った. あとドキュメントがロクに書かれていないのも辛かった.