d2s.hpp 8.7 KB

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