Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-13 09:41:39

0001 // Copyright 2020-2023 Daniel Lemire
0002 // Copyright 2023 Matt Borland
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // https://www.boost.org/LICENSE_1_0.txt
0005 //
0006 // Derivative of: https://github.com/fastfloat/fast_float
0007 
0008 #ifndef BOOST_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
0009 #define BOOST_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
0010 
0011 #include <boost/charconv/detail/fast_float/float_common.hpp>
0012 #include <algorithm>
0013 #include <cstdint>
0014 #include <climits>
0015 #include <cstring>
0016 
0017 namespace boost { namespace charconv { namespace detail { namespace fast_float {
0018 
0019 // the limb width: we want efficient multiplication of double the bits in
0020 // limb, or for 64-bit limbs, at least 64-bit multiplication where we can
0021 // extract the high and low parts efficiently. this is every 64-bit
0022 // architecture except for sparc, which emulates 128-bit multiplication.
0023 // we might have platforms where `CHAR_BIT` is not 8, so let's avoid
0024 // doing `8 * sizeof(limb)`.
0025 #if defined(BOOST_CHARCONV_FASTFLOAT_64BIT) && !defined(__sparc)
0026 #define BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB 1
0027 typedef uint64_t limb;
0028 constexpr size_t limb_bits = 64;
0029 #else
0030 #define BOOST_CHARCONV_FASTFLOAT_32BIT_LIMB
0031 typedef uint32_t limb;
0032 constexpr size_t limb_bits = 32;
0033 #endif
0034 
0035 typedef span<limb> limb_span;
0036 
0037 // number of bits in a bigint. this needs to be at least the number
0038 // of bits required to store the largest bigint, which is
0039 // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
0040 // ~3600 bits, so we round to 4000.
0041 constexpr size_t bigint_bits = 4000;
0042 constexpr size_t bigint_limbs = bigint_bits / limb_bits;
0043 
0044 // vector-like type that is allocated on the stack. the entire
0045 // buffer is pre-allocated, and only the length changes.
0046 template <uint16_t size>
0047 struct stackvec {
0048   limb data[size];
0049   // we never need more than 150 limbs
0050   uint16_t length{0};
0051 
0052   stackvec() = default;
0053   stackvec(const stackvec &) = delete;
0054   stackvec &operator=(const stackvec &) = delete;
0055   stackvec(stackvec &&) = delete;
0056   stackvec &operator=(stackvec &&other) = delete;
0057 
0058   // create stack vector from existing limb span.
0059   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
0060     BOOST_CHARCONV_FASTFLOAT_ASSERT(try_extend(s));
0061   }
0062 
0063   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
0064     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(index < length);
0065     return data[index];
0066   }
0067   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
0068     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(index < length);
0069     return data[index];
0070   }
0071   // index from the end of the container
0072   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
0073     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(index < length);
0074     size_t rindex = length - index - 1;
0075     return data[rindex];
0076   }
0077 
0078   // set the length, without bounds checking.
0079   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
0080     length = uint16_t(len);
0081   }
0082   constexpr size_t len() const noexcept {
0083     return length;
0084   }
0085   constexpr bool is_empty() const noexcept {
0086     return length == 0;
0087   }
0088   constexpr size_t capacity() const noexcept {
0089     return size;
0090   }
0091   // append item to vector, without bounds checking
0092   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
0093     data[length] = value;
0094     length++;
0095   }
0096   // append item to vector, returning if item was added
0097   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
0098     if (len() < capacity()) {
0099       push_unchecked(value);
0100       return true;
0101     } else {
0102       return false;
0103     }
0104   }
0105   // add items to the vector, from a span, without bounds checking
0106   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
0107     limb* ptr = data + length;
0108     std::copy_n(s.ptr, s.len(), ptr);
0109     set_len(len() + s.len());
0110   }
0111   // try to add items to the vector, returning if items were added
0112   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
0113     if (len() + s.len() <= capacity()) {
0114       extend_unchecked(s);
0115       return true;
0116     } else {
0117       return false;
0118     }
0119   }
0120   // resize the vector, without bounds checking
0121   // if the new size is longer than the vector, assign value to each
0122   // appended item.
0123   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0124   void resize_unchecked(size_t new_len, limb value) noexcept {
0125     if (new_len > len()) {
0126       size_t count = new_len - len();
0127       limb* first = data + len();
0128       limb* last = first + count;
0129       ::std::fill(first, last, value);
0130       set_len(new_len);
0131     } else {
0132       set_len(new_len);
0133     }
0134   }
0135   // try to resize the vector, returning if the vector was resized.
0136   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
0137     if (new_len > capacity()) {
0138       return false;
0139     } else {
0140       resize_unchecked(new_len, value);
0141       return true;
0142     }
0143   }
0144   // check if any limbs are non-zero after the given index.
0145   // this needs to be done in reverse order, since the index
0146   // is relative to the most significant limbs.
0147   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
0148     while (index < len()) {
0149       if (rindex(index) != 0) {
0150         return true;
0151       }
0152       index++;
0153     }
0154     return false;
0155   }
0156   // normalize the big integer, so most-significant zero limbs are removed.
0157   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
0158     while (len() > 0 && rindex(0) == 0) {
0159       length--;
0160     }
0161   }
0162 };
0163 
0164 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14
0165 uint64_t empty_hi64(bool& truncated) noexcept {
0166   truncated = false;
0167   return 0;
0168 }
0169 
0170 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0171 uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
0172   truncated = false;
0173   int shl = leading_zeroes(r0);
0174   return r0 << shl;
0175 }
0176 
0177 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0178 uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
0179   int shl = leading_zeroes(r0);
0180   if (shl == 0) {
0181     truncated = r1 != 0;
0182     return r0;
0183   } else {
0184     int shr = 64 - shl;
0185     truncated = (r1 << shl) != 0;
0186     return (r0 << shl) | (r1 >> shr);
0187   }
0188 }
0189 
0190 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0191 uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
0192   return uint64_hi64(r0, truncated);
0193 }
0194 
0195 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0196 uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
0197   uint64_t x0 = r0;
0198   uint64_t x1 = r1;
0199   return uint64_hi64((x0 << 32) | x1, truncated);
0200 }
0201 
0202 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0203 uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
0204   uint64_t x0 = r0;
0205   uint64_t x1 = r1;
0206   uint64_t x2 = r2;
0207   return uint64_hi64(x0, (x1 << 32) | x2, truncated);
0208 }
0209 
0210 // add two small integers, checking for overflow.
0211 // we want an efficient operation. for msvc, where
0212 // we don't have built-in intrinsics, this is still
0213 // pretty fast.
0214 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0215 limb scalar_add(limb x, limb y, bool& overflow) noexcept {
0216   limb z;
0217 // gcc and clang
0218 #if defined(__has_builtin)
0219   #if __has_builtin(__builtin_add_overflow)
0220     if (!cpp20_and_in_constexpr()) {
0221       overflow = __builtin_add_overflow(x, y, &z);
0222       return z;
0223     }
0224   #endif
0225 #endif
0226 
0227   // generic, this still optimizes correctly on MSVC.
0228   z = x + y;
0229   overflow = z < x;
0230   return z;
0231 }
0232 
0233 // multiply two small integers, getting both the high and low bits.
0234 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0235 limb scalar_mul(limb x, limb y, limb& carry) noexcept {
0236 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0237   #if defined(__SIZEOF_INT128__)
0238   // GCC and clang both define it as an extension.
0239   __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
0240   carry = limb(z >> limb_bits);
0241   return limb(z);
0242   #else
0243   // fallback, no native 128-bit integer multiplication with carry.
0244   // on msvc, this optimizes identically, somehow.
0245   value128 z = full_multiplication(x, y);
0246   bool overflow;
0247   z.low = scalar_add(z.low, carry, overflow);
0248   z.high += uint64_t(overflow);  // cannot overflow
0249   carry = z.high;
0250   return z.low;
0251   #endif
0252 #else
0253   uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
0254   carry = limb(z >> limb_bits);
0255   return limb(z);
0256 #endif
0257 }
0258 
0259 // add scalar value to bigint starting from offset.
0260 // used in grade school multiplication
0261 template <uint16_t size>
0262 inline BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0263 bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
0264   size_t index = start;
0265   limb carry = y;
0266   bool overflow;
0267   while (carry != 0 && index < vec.len()) {
0268     vec[index] = scalar_add(vec[index], carry, overflow);
0269     carry = limb(overflow);
0270     index += 1;
0271   }
0272   if (carry != 0) {
0273     BOOST_CHARCONV_FASTFLOAT_TRY(vec.try_push(carry));
0274   }
0275   return true;
0276 }
0277 
0278 // add scalar value to bigint.
0279 template <uint16_t size>
0280 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0281 bool small_add(stackvec<size>& vec, limb y) noexcept {
0282   return small_add_from(vec, y, 0);
0283 }
0284 
0285 // multiply bigint by scalar value.
0286 template <uint16_t size>
0287 inline BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0288 bool small_mul(stackvec<size>& vec, limb y) noexcept {
0289   limb carry = 0;
0290   for (size_t index = 0; index < vec.len(); index++) {
0291     vec[index] = scalar_mul(vec[index], y, carry);
0292   }
0293   if (carry != 0) {
0294     BOOST_CHARCONV_FASTFLOAT_TRY(vec.try_push(carry));
0295   }
0296   return true;
0297 }
0298 
0299 // add bigint to bigint starting from index.
0300 // used in grade school multiplication
0301 template <uint16_t size>
0302 BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0303 bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
0304   // the effective x buffer is from `xstart..x.len()`, so exit early
0305   // if we can't get that current range.
0306   if (x.len() < start || y.len() > x.len() - start) {
0307       BOOST_CHARCONV_FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
0308   }
0309 
0310   bool carry = false;
0311   for (size_t index = 0; index < y.len(); index++) {
0312     limb xi = x[index + start];
0313     limb yi = y[index];
0314     bool c1 = false;
0315     bool c2 = false;
0316     xi = scalar_add(xi, yi, c1);
0317     if (carry) {
0318       xi = scalar_add(xi, 1, c2);
0319     }
0320     x[index + start] = xi;
0321     carry = c1 | c2;
0322   }
0323 
0324   // handle overflow
0325   if (carry) {
0326     BOOST_CHARCONV_FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
0327   }
0328   return true;
0329 }
0330 
0331 // add bigint to bigint.
0332 template <uint16_t size>
0333 BOOST_FORCEINLINE BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0334 bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
0335   return large_add_from(x, y, 0);
0336 }
0337 
0338 // grade-school multiplication algorithm
0339 template <uint16_t size>
0340 BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0341 bool long_mul(stackvec<size>& x, limb_span y) noexcept {
0342   limb_span xs = limb_span(x.data, x.len());
0343   stackvec<size> z(xs);
0344   limb_span zs = limb_span(z.data, z.len());
0345 
0346   if (y.len() != 0) {
0347     limb y0 = y[0];
0348     BOOST_CHARCONV_FASTFLOAT_TRY(small_mul(x, y0));
0349     for (size_t index = 1; index < y.len(); index++) {
0350       limb yi = y[index];
0351       stackvec<size> zi;
0352       if (yi != 0) {
0353         // re-use the same buffer throughout
0354         zi.set_len(0);
0355         BOOST_CHARCONV_FASTFLOAT_TRY(zi.try_extend(zs));
0356         BOOST_CHARCONV_FASTFLOAT_TRY(small_mul(zi, yi));
0357         limb_span zis = limb_span(zi.data, zi.len());
0358         BOOST_CHARCONV_FASTFLOAT_TRY(large_add_from(x, zis, index));
0359       }
0360     }
0361   }
0362 
0363   x.normalize();
0364   return true;
0365 }
0366 
0367 // grade-school multiplication algorithm
0368 template <uint16_t size>
0369 BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0370 bool large_mul(stackvec<size>& x, limb_span y) noexcept {
0371   if (y.len() == 1) {
0372     BOOST_CHARCONV_FASTFLOAT_TRY(small_mul(x, y[0]));
0373   } else {
0374     BOOST_CHARCONV_FASTFLOAT_TRY(long_mul(x, y));
0375   }
0376   return true;
0377 }
0378 
0379 template <typename = void>
0380 struct pow5_tables {
0381   static constexpr uint32_t large_step = 135;
0382   static constexpr uint64_t small_power_of_5[] = {
0383     1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
0384     1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
0385     6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
0386     3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
0387     2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
0388     298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
0389   };
0390 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0391   constexpr static limb large_power_of_5[] = {
0392     1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
0393     10482974169319127550UL, 198276706040285095UL};
0394 #else
0395   constexpr static limb large_power_of_5[] = {
0396     4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
0397     1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
0398 #endif
0399 };
0400 
0401 template <typename T>
0402 constexpr uint32_t pow5_tables<T>::large_step;
0403 
0404 template <typename T>
0405 constexpr uint64_t pow5_tables<T>::small_power_of_5[];
0406 
0407 template <typename T>
0408 constexpr limb pow5_tables<T>::large_power_of_5[];
0409 
0410 // big integer type. implements a small subset of big integer
0411 // arithmetic, using simple algorithms since asymptotically
0412 // faster algorithms are slower for a small number of limbs.
0413 // all operations assume the big-integer is normalized.
0414 struct bigint : pow5_tables<> {
0415   // storage of the limbs, in little-endian order.
0416   stackvec<bigint_limbs> vec;
0417 
0418   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
0419   bigint(const bigint &) = delete;
0420   bigint &operator=(const bigint &) = delete;
0421   bigint(bigint &&) = delete;
0422   bigint &operator=(bigint &&other) = delete;
0423 
0424   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
0425 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0426     vec.push_unchecked(value);
0427 #else
0428     vec.push_unchecked(uint32_t(value));
0429     vec.push_unchecked(uint32_t(value >> 32));
0430 #endif
0431     vec.normalize();
0432   }
0433 
0434   // get the high 64 bits from the vector, and if bits were truncated.
0435   // this is to get the significant digits for the float.
0436   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
0437 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0438     if (vec.len() == 0) {
0439       return empty_hi64(truncated);
0440     } else if (vec.len() == 1) {
0441       return uint64_hi64(vec.rindex(0), truncated);
0442     } else {
0443       uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
0444       truncated |= vec.nonzero(2);
0445       return result;
0446     }
0447 #else
0448     if (vec.len() == 0) {
0449       return empty_hi64(truncated);
0450     } else if (vec.len() == 1) {
0451       return uint32_hi64(vec.rindex(0), truncated);
0452     } else if (vec.len() == 2) {
0453       return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
0454     } else {
0455       uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
0456       truncated |= vec.nonzero(3);
0457       return result;
0458     }
0459 #endif
0460   }
0461 
0462   // compare two big integers, returning the large value.
0463   // assumes both are normalized. if the return value is
0464   // negative, other is larger, if the return value is
0465   // positive, this is larger, otherwise they are equal.
0466   // the limbs are stored in little-endian order, so we
0467   // must compare the limbs in ever order.
0468   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
0469     if (vec.len() > other.vec.len()) {
0470       return 1;
0471     } else if (vec.len() < other.vec.len()) {
0472       return -1;
0473     } else {
0474       for (size_t index = vec.len(); index > 0; index--) {
0475         limb xi = vec[index - 1];
0476         limb yi = other.vec[index - 1];
0477         if (xi > yi) {
0478           return 1;
0479         } else if (xi < yi) {
0480           return -1;
0481         }
0482       }
0483       return 0;
0484     }
0485   }
0486 
0487   // shift left each limb n bits, carrying over to the new limb
0488   // returns true if we were able to shift all the digits.
0489   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
0490     // Internally, for each item, we shift left by n, and add the previous
0491     // right shifted limb-bits.
0492     // For example, we transform (for u8) shifted left 2, to:
0493     //      b10100100 b01000010
0494     //      b10 b10010001 b00001000
0495     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(n != 0);
0496     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
0497 
0498     size_t shl = n;
0499     size_t shr = limb_bits - shl;
0500     limb prev = 0;
0501     for (size_t index = 0; index < vec.len(); index++) {
0502       limb xi = vec[index];
0503       vec[index] = (xi << shl) | (prev >> shr);
0504       prev = xi;
0505     }
0506 
0507     limb carry = prev >> shr;
0508     if (carry != 0) {
0509       return vec.try_push(carry);
0510     }
0511     return true;
0512   }
0513 
0514   // move the limbs left by `n` limbs.
0515   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
0516     BOOST_CHARCONV_FASTFLOAT_DEBUG_ASSERT(n != 0);
0517     if (n + vec.len() > vec.capacity()) {
0518       return false;
0519     } else if (!vec.is_empty()) {
0520       // move limbs
0521       limb* dst = vec.data + n;
0522       const limb* src = vec.data;
0523       std::copy_backward(src, src + vec.len(), dst + vec.len());
0524       // fill in empty limbs
0525       limb* first = vec.data;
0526       limb* last = first + n;
0527       ::std::fill(first, last, 0);
0528       vec.set_len(n + vec.len());
0529       return true;
0530     } else {
0531       return true;
0532     }
0533   }
0534 
0535   // move the limbs left by `n` bits.
0536   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
0537     size_t rem = n % limb_bits;
0538     size_t div = n / limb_bits;
0539     if (rem != 0) {
0540       BOOST_CHARCONV_FASTFLOAT_TRY(shl_bits(rem));
0541     }
0542     if (div != 0) {
0543       BOOST_CHARCONV_FASTFLOAT_TRY(shl_limbs(div));
0544     }
0545     return true;
0546   }
0547 
0548   // get the number of leading zeros in the bigint.
0549   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
0550     if (vec.is_empty()) {
0551       return 0;
0552     } else {
0553 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0554       return leading_zeroes(vec.rindex(0));
0555 #else
0556       // no use defining a specialized leading_zeroes for a 32-bit type.
0557       uint64_t r0 = vec.rindex(0);
0558       return leading_zeroes(r0 << 32);
0559 #endif
0560     }
0561   }
0562 
0563   // get the number of bits in the bigint.
0564   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
0565     int lz = ctlz();
0566     return int(limb_bits * vec.len()) - lz;
0567   }
0568 
0569   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
0570     return small_mul(vec, y);
0571   }
0572 
0573   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
0574     return small_add(vec, y);
0575   }
0576 
0577   // multiply as if by 2 raised to a power.
0578   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
0579     return shl(exp);
0580   }
0581 
0582   // multiply as if by 5 raised to a power.
0583   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
0584     // multiply by a power of 5
0585     constexpr size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
0586     limb_span large = limb_span(large_power_of_5, large_length);
0587     while (exp >= large_step) {
0588       BOOST_CHARCONV_FASTFLOAT_TRY(large_mul(vec, large));
0589       exp -= large_step;
0590     }
0591 #ifdef BOOST_CHARCONV_FASTFLOAT_64BIT_LIMB
0592     constexpr uint32_t small_step = 27;
0593     constexpr limb max_native = 7450580596923828125UL;
0594 #else
0595     constexpr uint32_t small_step = 13;
0596     constexpr limb max_native = 1220703125U;
0597 #endif
0598     while (exp >= small_step) {
0599       BOOST_CHARCONV_FASTFLOAT_TRY(small_mul(vec, max_native));
0600       exp -= small_step;
0601     }
0602     if (exp != 0) {
0603       // Work around clang bug https://godbolt.org/z/zedh7rrhc
0604       // This is similar to https://github.com/llvm/llvm-project/issues/47746,
0605       // except the workaround described there don't work here
0606       BOOST_CHARCONV_FASTFLOAT_TRY(
0607         small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
0608       );
0609     }
0610 
0611     return true;
0612   }
0613 
0614   // multiply as if by 10 raised to a power.
0615   BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
0616     BOOST_CHARCONV_FASTFLOAT_TRY(pow5(exp));
0617     return pow2(exp);
0618   }
0619 };
0620 
0621 }}}} // namespace fast_float
0622 
0623 #endif