Line data Source code
1 : // Copyright 2018 Ulf Adams
2 : //
3 : // The contents of this file may be used under the terms of the Apache License,
4 : // Version 2.0.
5 : //
6 : // (See accompanying file LICENSE-Apache or copy at
7 : // http://www.apache.org/licenses/LICENSE-2.0)
8 : //
9 : // Alternatively, the contents of this file may be used under the terms of
10 : // the Boost Software License, Version 1.0.
11 : // (See accompanying file LICENSE-Boost or copy at
12 : // https://www.boost.org/LICENSE_1_0.txt)
13 : //
14 : // Unless required by applicable law or agreed to in writing, this software
15 : // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 : // KIND, either express or implied.
17 :
18 : /*
19 : This is a derivative work
20 : */
21 :
22 : #ifndef BOOST_JSON_DETAIL_RYU_DETAIL_D2S_HPP
23 : #define BOOST_JSON_DETAIL_RYU_DETAIL_D2S_HPP
24 :
25 : #include <boost/json/detail/config.hpp>
26 : #include <boost/json/detail/ryu/detail/common.hpp>
27 :
28 : // Only include the full table if we're not optimizing for size.
29 : #if !defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
30 : #include <boost/json/detail/ryu/detail/d2s_full_table.hpp>
31 : #endif
32 : #if defined(BOOST_JSON_RYU_HAS_UINT128)
33 : typedef __uint128_t uint128_t;
34 : #else
35 : #include <boost/json/detail/ryu/detail/d2s_intrinsics.hpp>
36 : #endif
37 :
38 : namespace boost {
39 : namespace json {
40 : namespace detail {
41 :
42 : namespace ryu {
43 : namespace detail {
44 :
45 : constexpr int DOUBLE_POW5_INV_BITCOUNT = 122;
46 : constexpr int DOUBLE_POW5_BITCOUNT = 121;
47 :
48 : #if defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
49 :
50 : constexpr int POW5_TABLE_SIZE = 26;
51 :
52 : inline
53 : std::uint64_t const
54 1186 : (&DOUBLE_POW5_TABLE() noexcept)[POW5_TABLE_SIZE]
55 : {
56 : static constexpr std::uint64_t arr[26] = {
57 : 1ull, 5ull, 25ull, 125ull, 625ull, 3125ull, 15625ull, 78125ull, 390625ull,
58 : 1953125ull, 9765625ull, 48828125ull, 244140625ull, 1220703125ull, 6103515625ull,
59 : 30517578125ull, 152587890625ull, 762939453125ull, 3814697265625ull,
60 : 19073486328125ull, 95367431640625ull, 476837158203125ull,
61 : 2384185791015625ull, 11920928955078125ull, 59604644775390625ull,
62 : 298023223876953125ull //, 1490116119384765625ull
63 : };
64 1186 : return arr;
65 : }
66 :
67 : inline
68 : std::uint64_t const
69 652 : (&DOUBLE_POW5_SPLIT2() noexcept)[13][2]
70 : {
71 : static constexpr std::uint64_t arr[13][2] = {
72 : { 0u, 72057594037927936u },
73 : { 10376293541461622784u, 93132257461547851u },
74 : { 15052517733678820785u, 120370621524202240u },
75 : { 6258995034005762182u, 77787690973264271u },
76 : { 14893927168346708332u, 100538234169297439u },
77 : { 4272820386026678563u, 129942622070561240u },
78 : { 7330497575943398595u, 83973451344588609u },
79 : { 18377130505971182927u, 108533142064701048u },
80 : { 10038208235822497557u, 140275798336537794u },
81 : { 7017903361312433648u, 90651109995611182u },
82 : { 6366496589810271835u, 117163813585596168u },
83 : { 9264989777501460624u, 75715339914673581u },
84 : { 17074144231291089770u, 97859783203563123u }};
85 652 : return arr;
86 : }
87 :
88 : // Unfortunately, the results are sometimes off by one. We use an additional
89 : // lookup table to store those cases and adjust the result.
90 : inline
91 : std::uint32_t const
92 626 : (&POW5_OFFSETS() noexcept)[13]
93 : {
94 : static constexpr std::uint32_t arr[13] = {
95 : 0x00000000, 0x00000000, 0x00000000, 0x033c55be, 0x03db77d8, 0x0265ffb2,
96 : 0x00000800, 0x01a8ff56, 0x00000000, 0x0037a200, 0x00004000, 0x03fffffc,
97 : 0x00003ffe};
98 626 : return arr;
99 : }
100 :
101 : inline
102 : std::uint64_t const
103 584 : (&DOUBLE_POW5_INV_SPLIT2() noexcept)[13][2]
104 : {
105 : static constexpr std::uint64_t arr[13][2] = {
106 : { 1u, 288230376151711744u },
107 : { 7661987648932456967u, 223007451985306231u },
108 : { 12652048002903177473u, 172543658669764094u },
109 : { 5522544058086115566u, 266998379490113760u },
110 : { 3181575136763469022u, 206579990246952687u },
111 : { 4551508647133041040u, 159833525776178802u },
112 : { 1116074521063664381u, 247330401473104534u },
113 : { 17400360011128145022u, 191362629322552438u },
114 : { 9297997190148906106u, 148059663038321393u },
115 : { 11720143854957885429u, 229111231347799689u },
116 : { 15401709288678291155u, 177266229209635622u },
117 : { 3003071137298187333u, 274306203439684434u },
118 : { 17516772882021341108u, 212234145163966538u }};
119 584 : return arr;
120 : }
121 :
122 : inline
123 : std::uint32_t const
124 560 : (&POW5_INV_OFFSETS() noexcept)[20]
125 : {
126 : static constexpr std::uint32_t arr[20] = {
127 : 0x51505404, 0x55054514, 0x45555545, 0x05511411, 0x00505010, 0x00000004,
128 : 0x00000000, 0x00000000, 0x55555040, 0x00505051, 0x00050040, 0x55554000,
129 : 0x51659559, 0x00001000, 0x15000010, 0x55455555, 0x41404051, 0x00001010,
130 : 0x00000014, 0x00000000};
131 560 : return arr;
132 : }
133 :
134 : #if defined(BOOST_JSON_RYU_HAS_UINT128)
135 :
136 : // Computes 5^i in the form required by Ryu, and stores it in the given pointer.
137 : inline
138 : void
139 652 : double_computePow5(
140 : const std::uint32_t i,
141 : std::uint64_t* const result)
142 : {
143 652 : const std::uint32_t base = i / POW5_TABLE_SIZE;
144 652 : const std::uint32_t base2 = base * POW5_TABLE_SIZE;
145 652 : const std::uint32_t offset = i - base2;
146 652 : const std::uint64_t* const mul = DOUBLE_POW5_SPLIT2()[base];
147 652 : if (offset == 0)
148 : {
149 26 : result[0] = mul[0];
150 26 : result[1] = mul[1];
151 26 : return;
152 : }
153 626 : const std::uint64_t m = DOUBLE_POW5_TABLE()[offset];
154 626 : const uint128_t b0 = ((uint128_t)m) * mul[0];
155 626 : const uint128_t b2 = ((uint128_t)m) * mul[1];
156 626 : const std::uint32_t delta = pow5bits(i) - pow5bits(base2);
157 626 : const uint128_t shiftedSum = (b0 >> delta) + (b2 << (64 - delta)) + ((POW5_OFFSETS()[base] >> offset) & 1);
158 626 : result[0] = (std::uint64_t)shiftedSum;
159 626 : result[1] = (std::uint64_t)(shiftedSum >> 64);
160 : }
161 :
162 : // Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
163 : inline
164 : void
165 584 : double_computeInvPow5(
166 : const std::uint32_t i,
167 : std::uint64_t* const result)
168 : {
169 584 : const std::uint32_t base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
170 584 : const std::uint32_t base2 = base * POW5_TABLE_SIZE;
171 584 : const std::uint32_t offset = base2 - i;
172 584 : const std::uint64_t* const mul = DOUBLE_POW5_INV_SPLIT2()[base]; // 1/5^base2
173 584 : if (offset == 0)
174 : {
175 24 : result[0] = mul[0];
176 24 : result[1] = mul[1];
177 24 : return;
178 : }
179 560 : const std::uint64_t m = DOUBLE_POW5_TABLE()[offset]; // 5^offset
180 560 : const uint128_t b0 = ((uint128_t)m) * (mul[0] - 1);
181 560 : const uint128_t b2 = ((uint128_t)m) * mul[1]; // 1/5^base2 * 5^offset = 1/5^(base2-offset) = 1/5^i
182 560 : const std::uint32_t delta = pow5bits(base2) - pow5bits(i);
183 : const uint128_t shiftedSum =
184 560 : ((b0 >> delta) + (b2 << (64 - delta))) + 1 + ((POW5_INV_OFFSETS()[i / 16] >> ((i % 16) << 1)) & 3);
185 560 : result[0] = (std::uint64_t)shiftedSum;
186 560 : result[1] = (std::uint64_t)(shiftedSum >> 64);
187 : }
188 :
189 : #else // defined(BOOST_JSON_RYU_HAS_UINT128)
190 :
191 : // Computes 5^i in the form required by Ryu, and stores it in the given pointer.
192 : inline
193 : void
194 : double_computePow5(
195 : const std::uint32_t i,
196 : std::uint64_t* const result)
197 : {
198 : const std::uint32_t base = i / POW5_TABLE_SIZE;
199 : const std::uint32_t base2 = base * POW5_TABLE_SIZE;
200 : const std::uint32_t offset = i - base2;
201 : const std::uint64_t* const mul = DOUBLE_POW5_SPLIT2()[base];
202 : if (offset == 0)
203 : {
204 : result[0] = mul[0];
205 : result[1] = mul[1];
206 : return;
207 : }
208 : std::uint64_t const m = DOUBLE_POW5_TABLE()[offset];
209 : std::uint64_t high1;
210 : std::uint64_t const low1 = umul128(m, mul[1], &high1);
211 : std::uint64_t high0;
212 : std::uint64_t const low0 = umul128(m, mul[0], &high0);
213 : std::uint64_t const sum = high0 + low1;
214 : if (sum < high0)
215 : ++high1; // overflow into high1
216 : // high1 | sum | low0
217 : std::uint32_t const delta = pow5bits(i) - pow5bits(base2);
218 : result[0] = shiftright128(low0, sum, delta) + ((POW5_OFFSETS()[base] >> offset) & 1);
219 : result[1] = shiftright128(sum, high1, delta);
220 : }
221 :
222 : // Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
223 : inline
224 : void
225 : double_computeInvPow5(
226 : const std::uint32_t i,
227 : std::uint64_t* const result)
228 : {
229 : const std::uint32_t base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
230 : const std::uint32_t base2 = base * POW5_TABLE_SIZE;
231 : const std::uint32_t offset = base2 - i;
232 : const std::uint64_t* const mul = DOUBLE_POW5_INV_SPLIT2()[base]; // 1/5^base2
233 : if (offset == 0)
234 : {
235 : result[0] = mul[0];
236 : result[1] = mul[1];
237 : return;
238 : }
239 : std::uint64_t const m = DOUBLE_POW5_TABLE()[offset];
240 : std::uint64_t high1;
241 : std::uint64_t const low1 = umul128(m, mul[1], &high1);
242 : std::uint64_t high0;
243 : std::uint64_t const low0 = umul128(m, mul[0] - 1, &high0);
244 : std::uint64_t const sum = high0 + low1;
245 : if (sum < high0)
246 : ++high1; // overflow into high1
247 : // high1 | sum | low0
248 : std::uint32_t const delta = pow5bits(base2) - pow5bits(i);
249 : result[0] = shiftright128(low0, sum, delta) + 1 + ((POW5_INV_OFFSETS()[i / 16] >> ((i % 16) << 1)) & 3);
250 : result[1] = shiftright128(sum, high1, delta);
251 : }
252 :
253 : #endif // defined(BOOST_JSON_RYU_HAS_UINT128)
254 :
255 : #endif // defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
256 :
257 : } // detail
258 : } // ryu
259 :
260 : } // detail
261 : } // namespace json
262 : } // namespace boost
263 :
264 : #endif
|