bigint.hpp 19 KB

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