Bug #9595 » 0002-Improve-floating-point-round-trip-for-Postgres.patch
src/Wt/Dbo/backend/Postgres.C | ||
---|---|---|
#include <cstring>
|
||
#include <ctime>
|
||
/*
|
||
* include charconv (s.b. in c++ 17, but sometimes available with c++ 14, as an extension)
|
||
* NOTE: Some implementations of charconv are incomplete, e.g. lack floating point.
|
||
* Therefore, a separate feature test, e.g. #ifdef __cpp_lib_to_chars, is still required
|
||
*/
|
||
#if defined __has_include
|
||
# if __has_include (<charconv>)
|
||
# include <charconv>
|
||
# ifdef __cpp_lib_to_chars
|
||
# define WT_CPP_LIB_TO_CHARS
|
||
# endif
|
||
# endif
|
||
#endif
|
||
#include <boost/config/warning_disable.hpp>
|
||
#include <boost/spirit/include/karma.hpp>
|
||
#include <boost/spirit/include/qi_parse.hpp>
|
||
... | ... | |
static inline std::string double_to_s(const double d)
|
||
{
|
||
#ifdef WT_CPP_LIB_TO_CHARS
|
||
std::array<char, 30> buf;
|
||
auto rv = std::to_chars(buf.data(), buf.data() + buf.size(), d);
|
||
if (rv.ec != std::errc())
|
||
throw std::invalid_argument(
|
||
"double_to_s: to_chars failed for double (as to_string() '" +
|
||
std::to_string(d) + "'), err: " + std::error_condition(rv.ec).message());
|
||
return std::string(buf.data(), rv.ptr - buf.data());
|
||
#else
|
||
char buf[30];
|
||
char *p = buf;
|
||
if (d != 0) {
|
||
... | ... | |
}
|
||
*p = '\0';
|
||
return std::string(buf, p);
|
||
#endif
|
||
}
|
||
static inline std::string float_to_s(const float f)
|
||
{
|
||
#ifdef WT_CPP_LIB_TO_CHARS
|
||
std::array<char, 30> buf;
|
||
auto rv = std::to_chars(buf.data(), buf.data() + buf.size(), f);
|
||
if (rv.ec != std::errc())
|
||
throw std::invalid_argument(
|
||
"float_to_s: to_chars failed for float (as to_string() '" +
|
||
std::to_string(f) + "'), err: " + std::error_condition(rv.ec).message());
|
||
return std::string(buf.data(), rv.ptr - buf.data());
|
||
#else
|
||
char buf[30];
|
||
char *p = buf;
|
||
if (f != 0) {
|
||
... | ... | |
}
|
||
*p = '\0';
|
||
return std::string(buf, p);
|
||
#endif
|
||
}
|
||
}
|
||
... | ... | |
if (PQgetisnull(result_, row_, column))
|
||
return false;
|
||
*value = convert<float>("stof", boost::spirit::float_, PQgetvalue(result_, row_, column));
|
||
#ifdef WT_CPP_LIB_TO_CHARS
|
||
std::string result_s = PQgetvalue(result_, row_, column);
|
||
// try to convert with from_chars which has good round-trip properties
|
||
auto rv = std::from_chars(result_s.data(), result_s.data() + result_s.size(), *value);
|
||
// fall-back to boost::spirit for "out of range", e.g. subnormals in some implementations
|
||
if (rv.ec == std::errc::result_out_of_range) {
|
||
*value = convert<float>("stof", boost::spirit::float_, PQgetvalue(result_, row_, column));
|
||
} else if (rv.ec != std::errc()) {
|
||
throw PostgresException(std::string("getResult: from_chars (float) of '") +
|
||
result_s + "' failed, err: " + std::error_condition(rv.ec).message());
|
||
}
|
||
#else
|
||
try {
|
||
// try to convert with std::stof for backwards-compatibility and better round-trip/precision
|
||
*value = std::stof(PQgetvalue(result_, row_, column));
|
||
} catch (std::out_of_range) {
|
||
// fall-back to boost::spirit for "out of range", e.g. subnormals in some implementations
|
||
*value = convert<float>("stof", boost::spirit::float_, PQgetvalue(result_, row_, column));
|
||
}
|
||
#endif
|
||
LOG_DEBUG(this << " result float " << column << " " << *value);
|
||
... | ... | |
if (PQgetisnull(result_, row_, column))
|
||
return false;
|
||
*value = convert<double>("stod", boost::spirit::double_, PQgetvalue(result_, row_, column));
|
||
#ifdef WT_CPP_LIB_TO_CHARS
|
||
std::string result_s = PQgetvalue(result_, row_, column);
|
||
// try to convert with from_chars which has good round-trip properties
|
||
auto rv = std::from_chars(result_s.data(), result_s.data() + result_s.size(), *value);
|
||
// fall-back on boost::spirit for "out of range", e.g. subnormals in some implementations
|
||
if (rv.ec == std::errc::result_out_of_range) {
|
||
*value = convert<double>("stod", boost::spirit::double_, PQgetvalue(result_, row_, column));
|
||
} else if (rv.ec != std::errc()) {
|
||
throw PostgresException(std::string("getResult: from_chars (double) of '") +
|
||
result_s + "' failed, err: " + std::error_condition(rv.ec).message());
|
||
}
|
||
#else
|
||
try {
|
||
// try to convert with std::stod for backwards-compatibility and better round-trip/precision
|
||
*value = std::stod(PQgetvalue(result_, row_, column));
|
||
} catch (std::out_of_range) {
|
||
// fall-back to boost::spirit for "out of range", e.g. subnormals in some implementations
|
||
*value = convert<double>("stod", boost::spirit::double_, PQgetvalue(result_, row_, column));
|
||
}
|
||
#endif
|
||
LOG_DEBUG(this << " result double " << column << " " << *value);
|
||
- « Previous
- 1
- 2
- Next »