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
25namespace boost { namespace charconv { namespace detail {
26
27#if BOOST_CHARCONV_LDBL_BITS > 64
28
29static 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
41static 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
53template <typename ResultType, typename Unsigned_Integer, typename ArrayPtr>
54inline 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
82template <typename Unsigned_Integer>
83inline __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
117template <typename ResultType, typename Unsigned_Integer>
118inline 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

source code of boost/libs/charconv/include/boost/charconv/detail/compute_float80.hpp