| 1 | // Copyright 2023 Matt Borland |
| 2 | // Distributed under the Boost Software License, Version 1.0. |
| 3 | // https://www.boost.org/LICENSE_1_0.txt |
| 4 | |
| 5 | #ifndef BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP |
| 6 | #define BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP |
| 7 | |
| 8 | #include <boost/charconv/detail/config.hpp> |
| 9 | #include <boost/charconv/detail/emulated128.hpp> |
| 10 | #include <boost/charconv/detail/bit_layouts.hpp> |
| 11 | #include <system_error> |
| 12 | #include <type_traits> |
| 13 | #include <limits> |
| 14 | #include <cstdint> |
| 15 | #include <cmath> |
| 16 | #include <climits> |
| 17 | #include <cfloat> |
| 18 | |
| 19 | #ifdef BOOST_CHARCONV_DEBUG_FLOAT128 |
| 20 | #include <iostream> |
| 21 | #include <iomanip> |
| 22 | #include <boost/charconv/detail/to_chars_integer_impl.hpp> |
| 23 | #endif |
| 24 | |
| 25 | namespace boost { namespace charconv { namespace detail { |
| 26 | |
| 27 | #if BOOST_CHARCONV_LDBL_BITS > 64 |
| 28 | |
| 29 | static constexpr long double powers_of_ten_ld[] = { |
| 30 | 1e0L, 1e1L, 1e2L, 1e3L, 1e4L, 1e5L, 1e6L, |
| 31 | 1e7L, 1e8L, 1e9L, 1e10L, 1e11L, 1e12L, 1e13L, |
| 32 | 1e14L, 1e15L, 1e16L, 1e17L, 1e18L, 1e19L, 1e20L, |
| 33 | 1e21L, 1e22L, 1e23L, 1e24L, 1e25L, 1e26L, 1e27L, |
| 34 | 1e28L, 1e29L, 1e30L, 1e31L, 1e32L, 1e33L, 1e34L, |
| 35 | 1e35L, 1e36L, 1e37L, 1e38L, 1e39L, 1e40L, 1e41L, |
| 36 | 1e42L, 1e43L, 1e44L, 1e45L, 1e46L, 1e47L, 1e48L, |
| 37 | 1e49L, 1e50L, 1e51L, 1e52L, 1e53L, 1e54L, 1e55L |
| 38 | }; |
| 39 | |
| 40 | #ifdef BOOST_CHARCONV_HAS_FLOAT128 |
| 41 | static constexpr __float128 powers_of_tenq[] = { |
| 42 | 1e0Q, 1e1Q, 1e2Q, 1e3Q, 1e4Q, 1e5Q, 1e6Q, |
| 43 | 1e7Q, 1e8Q, 1e9Q, 1e10Q, 1e11Q, 1e12Q, 1e13Q, |
| 44 | 1e14Q, 1e15Q, 1e16Q, 1e17Q, 1e18Q, 1e19Q, 1e20Q, |
| 45 | 1e21Q, 1e22Q, 1e23Q, 1e24Q, 1e25Q, 1e26Q, 1e27Q, |
| 46 | 1e28Q, 1e29Q, 1e30Q, 1e31Q, 1e32Q, 1e33Q, 1e34Q, |
| 47 | 1e35Q, 1e36Q, 1e37Q, 1e38Q, 1e39Q, 1e40Q, 1e41Q, |
| 48 | 1e42Q, 1e43Q, 1e44Q, 1e45Q, 1e46Q, 1e47Q, 1e48Q, |
| 49 | 1e49Q, 1e50Q, 1e51Q, 1e52Q, 1e53Q, 1e54Q, 1e55Q |
| 50 | }; |
| 51 | #endif |
| 52 | |
| 53 | template <typename ResultType, typename Unsigned_Integer, typename ArrayPtr> |
| 54 | inline ResultType fast_path(std::int64_t q, Unsigned_Integer w, bool negative, ArrayPtr table) noexcept |
| 55 | { |
| 56 | // The general idea is as follows. |
| 57 | // if 0 <= s <= 2^64 and if 10^0 <= p <= 10^27 |
| 58 | // Both s and p can be represented exactly |
| 59 | // because of this s*p and s/p will produce |
| 60 | // correctly rounded values |
| 61 | |
| 62 | auto ld = static_cast<ResultType>(w); |
| 63 | |
| 64 | if (q < 0) |
| 65 | { |
| 66 | ld /= table[-q]; |
| 67 | } |
| 68 | else |
| 69 | { |
| 70 | ld *= table[q]; |
| 71 | } |
| 72 | |
| 73 | if (negative) |
| 74 | { |
| 75 | ld = -ld; |
| 76 | } |
| 77 | |
| 78 | return ld; |
| 79 | } |
| 80 | |
| 81 | #ifdef BOOST_CHARCONV_HAS_FLOAT128 |
| 82 | template <typename Unsigned_Integer> |
| 83 | inline __float128 compute_float128(std::int64_t q, Unsigned_Integer w, bool negative, std::errc& success) noexcept |
| 84 | { |
| 85 | // GLIBC uses 2^-16444 but MPFR uses 2^-16445 as the smallest subnormal value for 80 bit |
| 86 | // 39 is the max number of digits in an uint128_t |
| 87 | static constexpr auto smallest_power = -4951 - 39; |
| 88 | static constexpr auto largest_power = 4932; |
| 89 | |
| 90 | if (-55 <= q && q <= 48 && w <= static_cast<Unsigned_Integer>(1) << 113) |
| 91 | { |
| 92 | success = std::errc(); |
| 93 | return fast_path<__float128>(q, w, negative, powers_of_tenq); |
| 94 | } |
| 95 | |
| 96 | if (w == 0) |
| 97 | { |
| 98 | success = std::errc(); |
| 99 | return negative ? -0.0Q : 0.0Q; |
| 100 | } |
| 101 | else if (q > largest_power) |
| 102 | { |
| 103 | success = std::errc::result_out_of_range; |
| 104 | return negative ? -HUGE_VALQ : HUGE_VALQ; |
| 105 | } |
| 106 | else if (q < smallest_power) |
| 107 | { |
| 108 | success = std::errc::result_out_of_range; |
| 109 | return negative ? -0.0Q : 0.0Q; |
| 110 | } |
| 111 | |
| 112 | success = std::errc::not_supported; |
| 113 | return 0; |
| 114 | } |
| 115 | #endif |
| 116 | |
| 117 | template <typename ResultType, typename Unsigned_Integer> |
| 118 | inline ResultType compute_float80(std::int64_t q, Unsigned_Integer w, bool negative, std::errc& success) noexcept |
| 119 | { |
| 120 | // GLIBC uses 2^-16444 but MPFR uses 2^-16445 as the smallest subnormal value for 80 bit |
| 121 | // 39 is the max number of digits in an uint128_t |
| 122 | static constexpr auto smallest_power = -4951 - 39; |
| 123 | static constexpr auto largest_power = 4932; |
| 124 | |
| 125 | // We start with a fast path |
| 126 | // It is an extension of what was described in Clinger WD. |
| 127 | // How to read floating point numbers accurately. |
| 128 | // ACM SIGPLAN Notices. 1990 |
| 129 | // https://dl.acm.org/doi/pdf/10.1145/93542.93557 |
| 130 | static constexpr auto clinger_max_exp = BOOST_CHARCONV_LDBL_BITS == 80 ? 27 : 48; // NOLINT : Only changes by platform |
| 131 | static constexpr auto clinger_min_exp = BOOST_CHARCONV_LDBL_BITS == 80 ? -34 : -55; // NOLINT |
| 132 | |
| 133 | if (clinger_min_exp <= q && q <= clinger_max_exp && w <= static_cast<Unsigned_Integer>(1) << 113) |
| 134 | { |
| 135 | success = std::errc(); |
| 136 | return fast_path<ResultType>(q, w, negative, powers_of_ten_ld); |
| 137 | } |
| 138 | |
| 139 | if (w == 0) |
| 140 | { |
| 141 | success = std::errc(); |
| 142 | return negative ? -0.0L : 0.0L; |
| 143 | } |
| 144 | else if (q > largest_power) |
| 145 | { |
| 146 | success = std::errc::result_out_of_range; |
| 147 | return negative ? -HUGE_VALL : HUGE_VALL; |
| 148 | } |
| 149 | else if (q < smallest_power) |
| 150 | { |
| 151 | success = std::errc::result_out_of_range; |
| 152 | return negative ? -0.0L : 0.0L; |
| 153 | } |
| 154 | |
| 155 | success = std::errc::not_supported; |
| 156 | return 0; |
| 157 | } |
| 158 | |
| 159 | #endif // BOOST_CHARCONV_LDBL_BITS > 64 |
| 160 | |
| 161 | }}} // Namespaces |
| 162 | |
| 163 | #endif // BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP |
| 164 | |