boost 1.72 の spirit x3 の double_ パーサを使用していたところ、どうも値がちょっとおかしい。
円周率の小数点以下16桁までの文字列 "3.1415926535897932" で試してみました。
VC14.16.27023 (Visual Studio 2017 15.9.22) 使用。
#include <boost/spirit/home/x3.hpp>
#include <iomanip>
#include <iostream>
#include <string>
double atof_x3_double_(std::string const& str)
{
std::string::const_iterator iter = str.begin();
double result;
bool success = boost::spirit::x3::parse(iter, str.end(), boost::spirit::x3::double_, result);
if (!success || iter != str.end())
throw std::runtime_error("Parse failed.");
return result;
}
void dump(std::ostream& os, double const& x)
{
typedef unsigned __int64 uint64;
BOOST_STATIC_ASSERT(sizeof(uint64) == sizeof(double));
os << std::hex << reinterpret_cast<uint64 const&>(x);
}
int main()
{
using namespace std;
try {
char const org[] = "3.1415926535897932";
double x = atof_x3_double_(org);
double a = atof(org);
cout << setprecision(17);
cout << "org: \t" << org << endl;
cout << "double_:\t" << x << '\t';
dump(cout, x);
cout << endl;
cout << "atof: \t" << a << '\t';
dump(cout, a);
cout << endl;
}
catch (exception const& x) {
cerr << x.what() << endl;
}
}
spirit に合わせて全体的に const は後置になってますが、どっちでも大丈夫です。
出力
org: 3.1415926535897932
double_: 3.1415926535897927 400921fb54442d17
atof: 3.1415926535897931 400921fb54442d18
atof を通した場合と比べて、下位1ビットの違いだけですが精度が悪いです。
なので、double_ でパースした部分を文字列として取り出して、それを std::stof に通した結果を返すパーサ my_double を定義してみました。
namespace x3 = boost::spirit::x3;
struct my_double_class {};
typedef x3::rule<my_double_class, double> my_double_type;
my_double_type const my_double = "my_double";
static std::string::const_iterator my_double_pos;
auto const my_double_save_pos =
[] (auto const& ctx) { my_double_pos = _where(ctx).begin(); };
auto const my_double_act =
[] (auto& ctx) {
_val(ctx) = std::stod(std::string(my_double_pos, _where(ctx).begin()));
};
auto const my_double_def =
x3::eps [my_double_save_pos]
>> x3::double_ [my_double_act]
;
BOOST_SPIRIT_DEFINE(my_double);
これで boost::spirit::x3::double_ の代わりに my_double を使えば atof と同じ値になります。効率はともかくとして。
[2020/06/12] stof だったところを stod に書き換えました。