Project

General

Profile

Bug #9595 » 0002-Improve-floating-point-round-trip-for-Postgres.patch

Bruce Toll, 01/10/2022 12:22 PM

View differences:

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);
(2-2/2)