compute_float80.hpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2023 Matt Borland
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // https://www.boost.org/LICENSE_1_0.txt
  4. #ifndef BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP
  5. #define BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP
  6. #include <boost/charconv/detail/config.hpp>
  7. #include <boost/charconv/detail/emulated128.hpp>
  8. #include <boost/charconv/detail/bit_layouts.hpp>
  9. #include <system_error>
  10. #include <type_traits>
  11. #include <limits>
  12. #include <cstdint>
  13. #include <cmath>
  14. #include <climits>
  15. #include <cfloat>
  16. #ifdef BOOST_CHARCONV_DEBUG_FLOAT128
  17. #include <iostream>
  18. #include <iomanip>
  19. #include <boost/charconv/detail/to_chars_integer_impl.hpp>
  20. #endif
  21. namespace boost { namespace charconv { namespace detail {
  22. #if BOOST_CHARCONV_LDBL_BITS > 64
  23. static constexpr long double powers_of_ten_ld[] = {
  24. 1e0L, 1e1L, 1e2L, 1e3L, 1e4L, 1e5L, 1e6L,
  25. 1e7L, 1e8L, 1e9L, 1e10L, 1e11L, 1e12L, 1e13L,
  26. 1e14L, 1e15L, 1e16L, 1e17L, 1e18L, 1e19L, 1e20L,
  27. 1e21L, 1e22L, 1e23L, 1e24L, 1e25L, 1e26L, 1e27L,
  28. 1e28L, 1e29L, 1e30L, 1e31L, 1e32L, 1e33L, 1e34L,
  29. 1e35L, 1e36L, 1e37L, 1e38L, 1e39L, 1e40L, 1e41L,
  30. 1e42L, 1e43L, 1e44L, 1e45L, 1e46L, 1e47L, 1e48L,
  31. 1e49L, 1e50L, 1e51L, 1e52L, 1e53L, 1e54L, 1e55L
  32. };
  33. template <typename ResultType, typename Unsigned_Integer, typename ArrayPtr>
  34. inline ResultType fast_path(std::int64_t q, Unsigned_Integer w, bool negative, ArrayPtr table) noexcept
  35. {
  36. // The general idea is as follows.
  37. // if 0 <= s <= 2^64 and if 10^0 <= p <= 10^27
  38. // Both s and p can be represented exactly
  39. // because of this s*p and s/p will produce
  40. // correctly rounded values
  41. auto ld = static_cast<ResultType>(w);
  42. if (q < 0)
  43. {
  44. ld /= table[-q];
  45. }
  46. else
  47. {
  48. ld *= table[q];
  49. }
  50. if (negative)
  51. {
  52. ld = -ld;
  53. }
  54. return ld;
  55. }
  56. template <typename ResultType, typename Unsigned_Integer>
  57. inline ResultType compute_float80(std::int64_t q, Unsigned_Integer w, bool negative, std::errc& success) noexcept
  58. {
  59. // GLIBC uses 2^-16444 but MPFR uses 2^-16445 as the smallest subnormal value for 80 bit
  60. // 39 is the max number of digits in an uint128_t
  61. static constexpr auto smallest_power = -4951 - 39;
  62. static constexpr auto largest_power = 4932;
  63. // We start with a fast path
  64. // It is an extension of what was described in Clinger WD.
  65. // How to read floating point numbers accurately.
  66. // ACM SIGPLAN Notices. 1990
  67. // https://dl.acm.org/doi/pdf/10.1145/93542.93557
  68. static constexpr auto clinger_max_exp = BOOST_CHARCONV_LDBL_BITS == 80 ? 27 : 48; // NOLINT : Only changes by platform
  69. static constexpr auto clinger_min_exp = BOOST_CHARCONV_LDBL_BITS == 80 ? -34 : -55; // NOLINT
  70. if (clinger_min_exp <= q && q <= clinger_max_exp && w <= static_cast<Unsigned_Integer>(1) << 113)
  71. {
  72. success = std::errc();
  73. return fast_path<ResultType>(q, w, negative, powers_of_ten_ld);
  74. }
  75. if (w == 0)
  76. {
  77. success = std::errc();
  78. return negative ? -0.0L : 0.0L;
  79. }
  80. else if (q > largest_power)
  81. {
  82. success = std::errc::result_out_of_range;
  83. return negative ? -HUGE_VALL : HUGE_VALL;
  84. }
  85. else if (q < smallest_power)
  86. {
  87. success = std::errc::result_out_of_range;
  88. return negative ? -0.0L : 0.0L;
  89. }
  90. success = std::errc::not_supported;
  91. return 0;
  92. }
  93. #endif // BOOST_CHARCONV_LDBL_BITS > 64
  94. }}} // Namespaces
  95. #endif // BOOST_CHARCONV_DETAIL_COMPUTE_FLOAT80_HPP