Line data Source code
1 : #ifndef FASTFLOAT_BIGINT_H
2 : #define FASTFLOAT_BIGINT_H
3 :
4 : #include <algorithm>
5 : #include <cstdint>
6 : #include <climits>
7 : #include <cstring>
8 :
9 : #include "float_common.h"
10 :
11 : namespace fast_float {
12 :
13 : // the limb width: we want efficient multiplication of double the bits in
14 : // limb, or for 64-bit limbs, at least 64-bit multiplication where we can
15 : // extract the high and low parts efficiently. this is every 64-bit
16 : // architecture except for sparc, which emulates 128-bit multiplication.
17 : // we might have platforms where `CHAR_BIT` is not 8, so let's avoid
18 : // doing `8 * sizeof(limb)`.
19 : #if defined(FASTFLOAT_64BIT) && !defined(__sparc)
20 : #define FASTFLOAT_64BIT_LIMB 1
21 : typedef uint64_t limb;
22 : constexpr size_t limb_bits = 64;
23 : #else
24 : #define FASTFLOAT_32BIT_LIMB
25 : typedef uint32_t limb;
26 : constexpr size_t limb_bits = 32;
27 : #endif
28 :
29 : typedef span<limb> limb_span;
30 :
31 : // number of bits in a bigint. this needs to be at least the number
32 : // of bits required to store the largest bigint, which is
33 : // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
34 : // ~3600 bits, so we round to 4000.
35 : constexpr size_t bigint_bits = 4000;
36 : constexpr size_t bigint_limbs = bigint_bits / limb_bits;
37 :
38 : // vector-like type that is allocated on the stack. the entire
39 : // buffer is pre-allocated, and only the length changes.
40 : template <uint16_t size> struct stackvec {
41 : limb data[size];
42 : // we never need more than 150 limbs
43 : uint16_t length{0};
44 :
45 0 : stackvec() = default;
46 : stackvec(const stackvec &) = delete;
47 : stackvec &operator=(const stackvec &) = delete;
48 : stackvec(stackvec &&) = delete;
49 : stackvec &operator=(stackvec &&other) = delete;
50 :
51 : // create stack vector from existing limb span.
52 0 : FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
53 0 : FASTFLOAT_ASSERT(try_extend(s));
54 0 : }
55 :
56 0 : FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
57 : FASTFLOAT_DEBUG_ASSERT(index < length);
58 0 : return data[index];
59 : }
60 0 : FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
61 : FASTFLOAT_DEBUG_ASSERT(index < length);
62 0 : return data[index];
63 : }
64 : // index from the end of the container
65 0 : FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
66 : FASTFLOAT_DEBUG_ASSERT(index < length);
67 0 : size_t rindex = length - index - 1;
68 0 : return data[rindex];
69 : }
70 :
71 : // set the length, without bounds checking.
72 0 : FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
73 0 : length = uint16_t(len);
74 0 : }
75 0 : constexpr size_t len() const noexcept { return length; }
76 0 : constexpr bool is_empty() const noexcept { return length == 0; }
77 0 : constexpr size_t capacity() const noexcept { return size; }
78 : // append item to vector, without bounds checking
79 0 : FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
80 0 : data[length] = value;
81 0 : length++;
82 0 : }
83 : // append item to vector, returning if item was added
84 0 : FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
85 0 : if (len() < capacity()) {
86 0 : push_unchecked(value);
87 0 : return true;
88 : } else {
89 0 : return false;
90 : }
91 : }
92 : // add items to the vector, from a span, without bounds checking
93 0 : FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
94 0 : limb *ptr = data + length;
95 0 : std::copy_n(s.ptr, s.len(), ptr);
96 0 : set_len(len() + s.len());
97 0 : }
98 : // try to add items to the vector, returning if items were added
99 0 : FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
100 0 : if (len() + s.len() <= capacity()) {
101 0 : extend_unchecked(s);
102 0 : return true;
103 : } else {
104 0 : return false;
105 : }
106 : }
107 : // resize the vector, without bounds checking
108 : // if the new size is longer than the vector, assign value to each
109 : // appended item.
110 : FASTFLOAT_CONSTEXPR20
111 0 : void resize_unchecked(size_t new_len, limb value) noexcept {
112 0 : if (new_len > len()) {
113 0 : size_t count = new_len - len();
114 0 : limb *first = data + len();
115 0 : limb *last = first + count;
116 0 : ::std::fill(first, last, value);
117 0 : set_len(new_len);
118 : } else {
119 0 : set_len(new_len);
120 : }
121 0 : }
122 : // try to resize the vector, returning if the vector was resized.
123 0 : FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
124 0 : if (new_len > capacity()) {
125 0 : return false;
126 : } else {
127 0 : resize_unchecked(new_len, value);
128 0 : return true;
129 : }
130 : }
131 : // check if any limbs are non-zero after the given index.
132 : // this needs to be done in reverse order, since the index
133 : // is relative to the most significant limbs.
134 0 : FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
135 0 : while (index < len()) {
136 0 : if (rindex(index) != 0) {
137 0 : return true;
138 : }
139 0 : index++;
140 : }
141 0 : return false;
142 : }
143 : // normalize the big integer, so most-significant zero limbs are removed.
144 0 : FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
145 0 : while (len() > 0 && rindex(0) == 0) {
146 0 : length--;
147 : }
148 0 : }
149 : };
150 :
151 : fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
152 : empty_hi64(bool &truncated) noexcept {
153 0 : truncated = false;
154 0 : return 0;
155 : }
156 :
157 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
158 : uint64_hi64(uint64_t r0, bool &truncated) noexcept {
159 0 : truncated = false;
160 0 : int shl = leading_zeroes(r0);
161 0 : return r0 << shl;
162 : }
163 :
164 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
165 : uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
166 0 : int shl = leading_zeroes(r0);
167 0 : if (shl == 0) {
168 0 : truncated = r1 != 0;
169 0 : return r0;
170 : } else {
171 0 : int shr = 64 - shl;
172 0 : truncated = (r1 << shl) != 0;
173 0 : return (r0 << shl) | (r1 >> shr);
174 : }
175 : }
176 :
177 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
178 : uint32_hi64(uint32_t r0, bool &truncated) noexcept {
179 : return uint64_hi64(r0, truncated);
180 : }
181 :
182 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
183 : uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
184 : uint64_t x0 = r0;
185 : uint64_t x1 = r1;
186 : return uint64_hi64((x0 << 32) | x1, truncated);
187 : }
188 :
189 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
190 : uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
191 : uint64_t x0 = r0;
192 : uint64_t x1 = r1;
193 : uint64_t x2 = r2;
194 : return uint64_hi64(x0, (x1 << 32) | x2, truncated);
195 : }
196 :
197 : // add two small integers, checking for overflow.
198 : // we want an efficient operation. for msvc, where
199 : // we don't have built-in intrinsics, this is still
200 : // pretty fast.
201 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
202 : scalar_add(limb x, limb y, bool &overflow) noexcept {
203 : limb z;
204 : // gcc and clang
205 : #if defined(__has_builtin)
206 : #if __has_builtin(__builtin_add_overflow)
207 : if (!cpp20_and_in_constexpr()) {
208 : overflow = __builtin_add_overflow(x, y, &z);
209 : return z;
210 : }
211 : #endif
212 : #endif
213 :
214 : // generic, this still optimizes correctly on MSVC.
215 0 : z = x + y;
216 0 : overflow = z < x;
217 0 : return z;
218 : }
219 :
220 : // multiply two small integers, getting both the high and low bits.
221 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
222 : scalar_mul(limb x, limb y, limb &carry) noexcept {
223 : #ifdef FASTFLOAT_64BIT_LIMB
224 : #if defined(__SIZEOF_INT128__)
225 : // GCC and clang both define it as an extension.
226 0 : __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
227 0 : carry = limb(z >> limb_bits);
228 0 : return limb(z);
229 : #else
230 : // fallback, no native 128-bit integer multiplication with carry.
231 : // on msvc, this optimizes identically, somehow.
232 : value128 z = full_multiplication(x, y);
233 : bool overflow;
234 : z.low = scalar_add(z.low, carry, overflow);
235 : z.high += uint64_t(overflow); // cannot overflow
236 : carry = z.high;
237 : return z.low;
238 : #endif
239 : #else
240 : uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
241 : carry = limb(z >> limb_bits);
242 : return limb(z);
243 : #endif
244 : }
245 :
246 : // add scalar value to bigint starting from offset.
247 : // used in grade school multiplication
248 : template <uint16_t size>
249 0 : inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
250 : size_t start) noexcept {
251 0 : size_t index = start;
252 0 : limb carry = y;
253 : bool overflow;
254 0 : while (carry != 0 && index < vec.len()) {
255 0 : vec[index] = scalar_add(vec[index], carry, overflow);
256 0 : carry = limb(overflow);
257 0 : index += 1;
258 : }
259 0 : if (carry != 0) {
260 0 : FASTFLOAT_TRY(vec.try_push(carry));
261 : }
262 0 : return true;
263 : }
264 :
265 : // add scalar value to bigint.
266 : template <uint16_t size>
267 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
268 : small_add(stackvec<size> &vec, limb y) noexcept {
269 0 : return small_add_from(vec, y, 0);
270 : }
271 :
272 : // multiply bigint by scalar value.
273 : template <uint16_t size>
274 0 : inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
275 : limb y) noexcept {
276 0 : limb carry = 0;
277 0 : for (size_t index = 0; index < vec.len(); index++) {
278 0 : vec[index] = scalar_mul(vec[index], y, carry);
279 : }
280 0 : if (carry != 0) {
281 0 : FASTFLOAT_TRY(vec.try_push(carry));
282 : }
283 0 : return true;
284 : }
285 :
286 : // add bigint to bigint starting from index.
287 : // used in grade school multiplication
288 : template <uint16_t size>
289 0 : FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
290 : size_t start) noexcept {
291 : // the effective x buffer is from `xstart..x.len()`, so exit early
292 : // if we can't get that current range.
293 0 : if (x.len() < start || y.len() > x.len() - start) {
294 0 : FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
295 : }
296 :
297 0 : bool carry = false;
298 0 : for (size_t index = 0; index < y.len(); index++) {
299 0 : limb xi = x[index + start];
300 0 : limb yi = y[index];
301 0 : bool c1 = false;
302 0 : bool c2 = false;
303 0 : xi = scalar_add(xi, yi, c1);
304 0 : if (carry) {
305 0 : xi = scalar_add(xi, 1, c2);
306 : }
307 0 : x[index + start] = xi;
308 0 : carry = c1 | c2;
309 : }
310 :
311 : // handle overflow
312 0 : if (carry) {
313 0 : FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
314 : }
315 0 : return true;
316 : }
317 :
318 : // add bigint to bigint.
319 : template <uint16_t size>
320 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
321 : large_add_from(stackvec<size> &x, limb_span y) noexcept {
322 : return large_add_from(x, y, 0);
323 : }
324 :
325 : // grade-school multiplication algorithm
326 : template <uint16_t size>
327 0 : FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
328 0 : limb_span xs = limb_span(x.data, x.len());
329 0 : stackvec<size> z(xs);
330 0 : limb_span zs = limb_span(z.data, z.len());
331 :
332 0 : if (y.len() != 0) {
333 0 : limb y0 = y[0];
334 0 : FASTFLOAT_TRY(small_mul(x, y0));
335 0 : for (size_t index = 1; index < y.len(); index++) {
336 0 : limb yi = y[index];
337 0 : stackvec<size> zi;
338 0 : if (yi != 0) {
339 : // re-use the same buffer throughout
340 0 : zi.set_len(0);
341 0 : FASTFLOAT_TRY(zi.try_extend(zs));
342 0 : FASTFLOAT_TRY(small_mul(zi, yi));
343 0 : limb_span zis = limb_span(zi.data, zi.len());
344 0 : FASTFLOAT_TRY(large_add_from(x, zis, index));
345 : }
346 : }
347 : }
348 :
349 0 : x.normalize();
350 0 : return true;
351 : }
352 :
353 : // grade-school multiplication algorithm
354 : template <uint16_t size>
355 0 : FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
356 0 : if (y.len() == 1) {
357 0 : FASTFLOAT_TRY(small_mul(x, y[0]));
358 : } else {
359 0 : FASTFLOAT_TRY(long_mul(x, y));
360 : }
361 0 : return true;
362 : }
363 :
364 : template <typename = void> struct pow5_tables {
365 : static constexpr uint32_t large_step = 135;
366 : static constexpr uint64_t small_power_of_5[] = {
367 : 1UL,
368 : 5UL,
369 : 25UL,
370 : 125UL,
371 : 625UL,
372 : 3125UL,
373 : 15625UL,
374 : 78125UL,
375 : 390625UL,
376 : 1953125UL,
377 : 9765625UL,
378 : 48828125UL,
379 : 244140625UL,
380 : 1220703125UL,
381 : 6103515625UL,
382 : 30517578125UL,
383 : 152587890625UL,
384 : 762939453125UL,
385 : 3814697265625UL,
386 : 19073486328125UL,
387 : 95367431640625UL,
388 : 476837158203125UL,
389 : 2384185791015625UL,
390 : 11920928955078125UL,
391 : 59604644775390625UL,
392 : 298023223876953125UL,
393 : 1490116119384765625UL,
394 : 7450580596923828125UL,
395 : };
396 : #ifdef FASTFLOAT_64BIT_LIMB
397 : constexpr static limb large_power_of_5[] = {
398 : 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
399 : 10482974169319127550UL, 198276706040285095UL};
400 : #else
401 : constexpr static limb large_power_of_5[] = {
402 : 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
403 : 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
404 : #endif
405 : };
406 :
407 : #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
408 :
409 : template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
410 :
411 : template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
412 :
413 : template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[];
414 :
415 : #endif
416 :
417 : // big integer type. implements a small subset of big integer
418 : // arithmetic, using simple algorithms since asymptotically
419 : // faster algorithms are slower for a small number of limbs.
420 : // all operations assume the big-integer is normalized.
421 : struct bigint : pow5_tables<> {
422 : // storage of the limbs, in little-endian order.
423 : stackvec<bigint_limbs> vec;
424 :
425 0 : FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
426 : bigint(const bigint &) = delete;
427 : bigint &operator=(const bigint &) = delete;
428 : bigint(bigint &&) = delete;
429 : bigint &operator=(bigint &&other) = delete;
430 :
431 0 : FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
432 : #ifdef FASTFLOAT_64BIT_LIMB
433 0 : vec.push_unchecked(value);
434 : #else
435 : vec.push_unchecked(uint32_t(value));
436 : vec.push_unchecked(uint32_t(value >> 32));
437 : #endif
438 0 : vec.normalize();
439 0 : }
440 :
441 : // get the high 64 bits from the vector, and if bits were truncated.
442 : // this is to get the significant digits for the float.
443 0 : FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept {
444 : #ifdef FASTFLOAT_64BIT_LIMB
445 0 : if (vec.len() == 0) {
446 0 : return empty_hi64(truncated);
447 0 : } else if (vec.len() == 1) {
448 0 : return uint64_hi64(vec.rindex(0), truncated);
449 : } else {
450 0 : uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
451 0 : truncated |= vec.nonzero(2);
452 0 : return result;
453 : }
454 : #else
455 : if (vec.len() == 0) {
456 : return empty_hi64(truncated);
457 : } else if (vec.len() == 1) {
458 : return uint32_hi64(vec.rindex(0), truncated);
459 : } else if (vec.len() == 2) {
460 : return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
461 : } else {
462 : uint64_t result =
463 : uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
464 : truncated |= vec.nonzero(3);
465 : return result;
466 : }
467 : #endif
468 : }
469 :
470 : // compare two big integers, returning the large value.
471 : // assumes both are normalized. if the return value is
472 : // negative, other is larger, if the return value is
473 : // positive, this is larger, otherwise they are equal.
474 : // the limbs are stored in little-endian order, so we
475 : // must compare the limbs in ever order.
476 0 : FASTFLOAT_CONSTEXPR20 int compare(const bigint &other) const noexcept {
477 0 : if (vec.len() > other.vec.len()) {
478 0 : return 1;
479 0 : } else if (vec.len() < other.vec.len()) {
480 0 : return -1;
481 : } else {
482 0 : for (size_t index = vec.len(); index > 0; index--) {
483 0 : limb xi = vec[index - 1];
484 0 : limb yi = other.vec[index - 1];
485 0 : if (xi > yi) {
486 0 : return 1;
487 0 : } else if (xi < yi) {
488 0 : return -1;
489 : }
490 : }
491 0 : return 0;
492 : }
493 : }
494 :
495 : // shift left each limb n bits, carrying over to the new limb
496 : // returns true if we were able to shift all the digits.
497 0 : FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
498 : // Internally, for each item, we shift left by n, and add the previous
499 : // right shifted limb-bits.
500 : // For example, we transform (for u8) shifted left 2, to:
501 : // b10100100 b01000010
502 : // b10 b10010001 b00001000
503 : FASTFLOAT_DEBUG_ASSERT(n != 0);
504 : FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
505 :
506 0 : size_t shl = n;
507 0 : size_t shr = limb_bits - shl;
508 0 : limb prev = 0;
509 0 : for (size_t index = 0; index < vec.len(); index++) {
510 0 : limb xi = vec[index];
511 0 : vec[index] = (xi << shl) | (prev >> shr);
512 0 : prev = xi;
513 : }
514 :
515 0 : limb carry = prev >> shr;
516 0 : if (carry != 0) {
517 0 : return vec.try_push(carry);
518 : }
519 0 : return true;
520 : }
521 :
522 : // move the limbs left by `n` limbs.
523 0 : FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
524 : FASTFLOAT_DEBUG_ASSERT(n != 0);
525 0 : if (n + vec.len() > vec.capacity()) {
526 0 : return false;
527 0 : } else if (!vec.is_empty()) {
528 : // move limbs
529 0 : limb *dst = vec.data + n;
530 0 : const limb *src = vec.data;
531 0 : std::copy_backward(src, src + vec.len(), dst + vec.len());
532 : // fill in empty limbs
533 0 : limb *first = vec.data;
534 0 : limb *last = first + n;
535 0 : ::std::fill(first, last, 0);
536 0 : vec.set_len(n + vec.len());
537 0 : return true;
538 : } else {
539 0 : return true;
540 : }
541 : }
542 :
543 : // move the limbs left by `n` bits.
544 0 : FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
545 0 : size_t rem = n % limb_bits;
546 0 : size_t div = n / limb_bits;
547 0 : if (rem != 0) {
548 0 : FASTFLOAT_TRY(shl_bits(rem));
549 : }
550 0 : if (div != 0) {
551 0 : FASTFLOAT_TRY(shl_limbs(div));
552 : }
553 0 : return true;
554 : }
555 :
556 : // get the number of leading zeros in the bigint.
557 0 : FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
558 0 : if (vec.is_empty()) {
559 0 : return 0;
560 : } else {
561 : #ifdef FASTFLOAT_64BIT_LIMB
562 0 : return leading_zeroes(vec.rindex(0));
563 : #else
564 : // no use defining a specialized leading_zeroes for a 32-bit type.
565 : uint64_t r0 = vec.rindex(0);
566 : return leading_zeroes(r0 << 32);
567 : #endif
568 : }
569 : }
570 :
571 : // get the number of bits in the bigint.
572 0 : FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
573 0 : int lz = ctlz();
574 0 : return int(limb_bits * vec.len()) - lz;
575 : }
576 :
577 0 : FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
578 :
579 0 : FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
580 :
581 : // multiply as if by 2 raised to a power.
582 0 : FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
583 :
584 : // multiply as if by 5 raised to a power.
585 0 : FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
586 : // multiply by a power of 5
587 0 : size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
588 0 : limb_span large = limb_span(large_power_of_5, large_length);
589 0 : while (exp >= large_step) {
590 0 : FASTFLOAT_TRY(large_mul(vec, large));
591 0 : exp -= large_step;
592 : }
593 : #ifdef FASTFLOAT_64BIT_LIMB
594 0 : uint32_t small_step = 27;
595 0 : limb max_native = 7450580596923828125UL;
596 : #else
597 : uint32_t small_step = 13;
598 : limb max_native = 1220703125U;
599 : #endif
600 0 : while (exp >= small_step) {
601 0 : FASTFLOAT_TRY(small_mul(vec, max_native));
602 0 : exp -= small_step;
603 : }
604 0 : if (exp != 0) {
605 : // Work around clang bug https://godbolt.org/z/zedh7rrhc
606 : // This is similar to https://github.com/llvm/llvm-project/issues/47746,
607 : // except the workaround described there don't work here
608 0 : FASTFLOAT_TRY(small_mul(
609 : vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
610 : }
611 :
612 0 : return true;
613 : }
614 :
615 : // multiply as if by 10 raised to a power.
616 0 : FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
617 0 : FASTFLOAT_TRY(pow5(exp));
618 0 : return pow2(exp);
619 : }
620 : };
621 :
622 : } // namespace fast_float
623 :
624 : #endif
|