diff --git a/include/boost/charconv/detail/compute_float80.hpp b/include/boost/charconv/detail/compute_float80.hpp index 8a239c5..bf87970 100644 --- a/include/boost/charconv/detail/compute_float80.hpp +++ b/include/boost/charconv/detail/compute_float80.hpp @@ -12,6 +12,68 @@ namespace boost { namespace charconv { namespace detail { +static constexpr long double powers_of_ten[] = { + 1e0L, 1e1L, 1e2L, 1e3L, 1e4L, 1e5L, 1e6L, + 1e7L, 1e8L, 1e9L, 1e10L, 1e11L, 1e12L, 1e13L, + 1e14L, 1e15L, 1e16L, 1e17L, 1e18L, 1e19L, 1e20L, + 1e21L, 1e22L, 1e23L, 1e24L, 1e25L, 1e26L, 1e27L +}; + +template +inline long double compute_float80(std::int64_t power, Unsigned_Integer i, bool negative, bool& success) noexcept +{ + // GLIBC uses 2^-16444 but MPFR uses 2^-16445 as the smallest subnormal value for 80 bit + static constexpr auto smallest_power = -4951; + static constexpr auto largest_power = 4931; + + // We start with a fast path + // It is an extension of what was described in Clinger WD. + // How to read floating point numbers accurately. + // ACM SIGPLAN Notices. 1990 + // https://dl.acm.org/doi/pdf/10.1145/93542.93557 + #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + if (0 <= power && power <= 27 && i <= static_cast(1) << 64) + #else + if (-27 <= power && power <= 27 && i <= static_cast(1) << 64) + #endif + { + // The general idea is as follows. + // if 0 <= s <= 2^64 and if 10^0 <= p <= 10^27 + // Both s and p can be represented exactly + // because of this s*p and s/p will produce + // correctly rounded values + + auto ld = static_cast(i); + + if (power < 0) + { + ld /= powers_of_ten[-power]; + } + else + { + ld *= powers_of_ten[power]; + } + + if (negative) + { + ld = -ld; + } + + success = true; + return ld; + } + + if (i == 0 || power < smallest_power) + { + return negative ? -0.0L : 0.0L; + } + else if (power > largest_power) + { + return negative ? -HUGE_VALL : HUGE_VALL; + } +} + +/* inline long double compute_float80(std::int64_t power, std::uint64_t i, bool negative, bool& success) noexcept { long double return_val; @@ -40,7 +102,7 @@ inline long double compute_float80(std::int64_t power, std::uint64_t i, bool neg success = true; return return_val; } - +*/ }}} // Namespaces #endif // BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP