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