// Copyright 2020-2024 Junekey Jeon
//
// The contents of this file may be used under the terms of
// the Apache License v2.0 with LLVM Exceptions.
//
//    (See accompanying file LICENSE-Apache or copy at
//     https://llvm.org/foundation/relicensing/LICENSE.txt)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE-Boost or copy at
//     https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.


#ifndef JKJ_HEADER_DRAGONBOX
#define JKJ_HEADER_DRAGONBOX

// Attribute for storing static data into a dedicated place, e.g. flash memory. Every ODR-used
// static data declaration will be decorated with this macro. The users may define this macro,
// before including the library headers, into whatever they want.
#ifndef JKJ_STATIC_DATA_SECTION
    #define JKJ_STATIC_DATA_SECTION
#else
    #define JKJ_STATIC_DATA_SECTION_DEFINED 1
#endif

// To use the library with toolchains without standard C++ headers, the users may define this macro
// into their custom namespace which contains the definitions of all the standard C++ library
// features used in this header. (The list can be found below.)
#ifndef JKJ_STD_REPLACEMENT_NAMESPACE
    #define JKJ_STD_REPLACEMENT_NAMESPACE std
    #include <cassert>
    #include <cstdint>
    #include <cstring>
    #include <limits>
    #include <type_traits>

    #ifdef __has_include
        #if __has_include(<version>)
            #include <version>
        #endif
    #endif
#else
    #define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 1
#endif

////////////////////////////////////////////////////////////////////////////////////////
// Language feature detections.
////////////////////////////////////////////////////////////////////////////////////////

// C++14 constexpr
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304L
    #define JKJ_HAS_CONSTEXPR14 1
#elif __cplusplus >= 201402L
    #define JKJ_HAS_CONSTEXPR14 1
#elif defined(_MSC_VER) && _MSC_VER >= 1910 && _MSVC_LANG >= 201402L
    #define JKJ_HAS_CONSTEXPR14 1
#else
    #define JKJ_HAS_CONSTEXPR14 0
#endif

#if JKJ_HAS_CONSTEXPR14
    #define JKJ_CONSTEXPR14 constexpr
#else
    #define JKJ_CONSTEXPR14
#endif

// C++17 constexpr lambdas
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201603L
    #define JKJ_HAS_CONSTEXPR17 1
#elif __cplusplus >= 201703L
    #define JKJ_HAS_CONSTEXPR17 1
#elif defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L
    #define JKJ_HAS_CONSTEXPR17 1
#else
    #define JKJ_HAS_CONSTEXPR17 0
#endif

// C++17 inline variables
#if defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L
    #define JKJ_HAS_INLINE_VARIABLE 1
#elif __cplusplus >= 201703L
    #define JKJ_HAS_INLINE_VARIABLE 1
#elif defined(_MSC_VER) && _MSC_VER >= 1912 && _MSVC_LANG >= 201703L
    #define JKJ_HAS_INLINE_VARIABLE 1
#else
    #define JKJ_HAS_INLINE_VARIABLE 0
#endif

#if JKJ_HAS_INLINE_VARIABLE
    #define JKJ_INLINE_VARIABLE inline constexpr
#else
    #define JKJ_INLINE_VARIABLE static constexpr
#endif

// C++17 if constexpr
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
    #define JKJ_HAS_IF_CONSTEXPR 1
#elif __cplusplus >= 201703L
    #define JKJ_HAS_IF_CONSTEXPR 1
#elif defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L
    #define JKJ_HAS_IF_CONSTEXPR 1
#else
    #define JKJ_HAS_IF_CONSTEXPR 0
#endif

#if JKJ_HAS_IF_CONSTEXPR
    #define JKJ_IF_CONSTEXPR if constexpr
#else
    #define JKJ_IF_CONSTEXPR if
#endif

// C++20 std::bit_cast
#if JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED
    #if JKJ_STD_REPLACEMENT_HAS_BIT_CAST
        #define JKJ_HAS_BIT_CAST 1
    #else
        #define JKJ_HAS_BIT_CAST 0
    #endif
#elif defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
    #include <bit>
    #define JKJ_HAS_BIT_CAST 1
#else
    #define JKJ_HAS_BIT_CAST 0
#endif

// C++23 if consteval or C++20 std::is_constant_evaluated
#if defined(__cpp_if_consteval) && __cpp_is_consteval >= 202106L
    #define JKJ_IF_CONSTEVAL if consteval
    #define JKJ_IF_NOT_CONSTEVAL if !consteval
    #define JKJ_CAN_BRANCH_ON_CONSTEVAL 1
    #define JKJ_USE_IS_CONSTANT_EVALUATED 0
#elif JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED
    #if JKJ_STD_REPLACEMENT_HAS_IS_CONSTANT_EVALUATED
        #define JKJ_IF_CONSTEVAL if (stdr::is_constant_evaluated())
        #define JKJ_IF_NOT_CONSTEVAL if (!stdr::is_constant_evaluated())
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 1
        #define JKJ_USE_IS_CONSTANT_EVALUATED 1
    #elif JKJ_HAS_IF_CONSTEXPR
        #define JKJ_IF_CONSTEVAL if constexpr (false)
        #define JKJ_IF_NOT_CONSTEVAL if constexpr (true)
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 0
        #define JKJ_USE_IS_CONSTANT_EVALUATED 0
    #else
        #define JKJ_IF_CONSTEVAL if (false)
        #define JKJ_IF_NOT_CONSTEVAL if (true)
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 0
        #define JKJ_USE_IS_CONSTANT_EVALUATED 0
    #endif
#else
    #if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
        #define JKJ_IF_CONSTEVAL if (stdr::is_constant_evaluated())
        #define JKJ_IF_NOT_CONSTEVAL if (!stdr::is_constant_evaluated())
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 1
        #define JKJ_USE_IS_CONSTANT_EVALUATED 1
    #elif JKJ_HAS_IF_CONSTEXPR
        #define JKJ_IF_CONSTEVAL if constexpr (false)
        #define JKJ_IF_NOT_CONSTEVAL if constexpr (true)
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 0
        #define JKJ_USE_IS_CONSTANT_EVALUATED 0
    #else
        #define JKJ_IF_CONSTEVAL if (false)
        #define JKJ_IF_NOT_CONSTEVAL if (true)
        #define JKJ_CAN_BRANCH_ON_CONSTEVAL 0
        #define JKJ_USE_IS_CONSTANT_EVALUATED 0
    #endif
#endif

#if JKJ_CAN_BRANCH_ON_CONSTEVAL && JKJ_HAS_BIT_CAST
    #define JKJ_CONSTEXPR20 constexpr
#else
    #define JKJ_CONSTEXPR20
#endif

// Suppress additional buffer overrun check.
// I have no idea why MSVC thinks some functions here are vulnerable to the buffer overrun
// attacks. No, they aren't.
#if defined(__GNUC__) || defined(__clang__)
    #define JKJ_SAFEBUFFERS
    #define JKJ_FORCEINLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
    #define JKJ_SAFEBUFFERS __declspec(safebuffers)
    #define JKJ_FORCEINLINE __forceinline
#else
    #define JKJ_SAFEBUFFERS
    #define JKJ_FORCEINLINE inline
#endif

#if defined(__has_builtin)
    #define JKJ_HAS_BUILTIN(x) __has_builtin(x)
#else
    #define JKJ_HAS_BUILTIN(x) false
#endif

#if defined(_MSC_VER)
    #include <intrin.h>
#elif defined(__INTEL_COMPILER)
    #include <immintrin.h>
#endif

namespace jkj {
    namespace dragonbox {
        ////////////////////////////////////////////////////////////////////////////////////////
        // The Compatibility layer for toolchains without standard C++ headers.
        ////////////////////////////////////////////////////////////////////////////////////////
        namespace detail {
            namespace stdr {
                // <bit>
#if JKJ_HAS_BIT_CAST
                using JKJ_STD_REPLACEMENT_NAMESPACE::bit_cast;
#endif

                // <cassert>
                // We need assert() macro, but it is not namespaced anyway, so nothing to do here.

                // <cstdint>
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_least8_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_least16_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_least32_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_fast8_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_fast16_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::int_fast32_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_least8_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_least16_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_least32_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_least64_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_fast8_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_fast16_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::uint_fast32_t;
                // We need INT32_C, UINT32_C and UINT64_C macros too, but again there is nothing to do
                // here.

                // <cstring>
                using JKJ_STD_REPLACEMENT_NAMESPACE::size_t;
                using JKJ_STD_REPLACEMENT_NAMESPACE::memcpy;

                // <limits>
                template <class T>
                using numeric_limits = JKJ_STD_REPLACEMENT_NAMESPACE::numeric_limits<T>;

                // <type_traits>
                template <bool cond, class T = void>
                using enable_if = JKJ_STD_REPLACEMENT_NAMESPACE::enable_if<cond, T>;
                template <class T>
                using add_rvalue_reference = JKJ_STD_REPLACEMENT_NAMESPACE::add_rvalue_reference<T>;
                template <bool cond, class T_true, class T_false>
                using conditional = JKJ_STD_REPLACEMENT_NAMESPACE::conditional<cond, T_true, T_false>;
#if JKJ_USE_IS_CONSTANT_EVALUATED
                using JKJ_STD_REPLACEMENT_NAMESPACE::is_constant_evaluated;
#endif
                template <class T1, class T2>
                using is_same = JKJ_STD_REPLACEMENT_NAMESPACE::is_same<T1, T2>;
#if !JKJ_HAS_BIT_CAST
                template <class T>
                using is_trivially_copyable = JKJ_STD_REPLACEMENT_NAMESPACE::is_trivially_copyable<T>;
#endif
                template <class T>
                using is_integral = JKJ_STD_REPLACEMENT_NAMESPACE::is_integral<T>;
                template <class T>
                using is_signed = JKJ_STD_REPLACEMENT_NAMESPACE::is_signed<T>;
                template <class T>
                using is_unsigned = JKJ_STD_REPLACEMENT_NAMESPACE::is_unsigned<T>;
            }
        }


        ////////////////////////////////////////////////////////////////////////////////////////
        // Some general utilities for C++11-compatibility.
        ////////////////////////////////////////////////////////////////////////////////////////
        namespace detail {
#if !JKJ_HAS_CONSTEXPR17
            template <stdr::size_t... indices>
            struct index_sequence {};

            template <stdr::size_t current, stdr::size_t total, class Dummy, stdr::size_t... indices>
            struct make_index_sequence_impl {
                using type = typename make_index_sequence_impl<current + 1, total, Dummy, indices...,
                                                               current>::type;
            };

            template <stdr::size_t total, class Dummy, stdr::size_t... indices>
            struct make_index_sequence_impl<total, total, Dummy, indices...> {
                using type = index_sequence<indices...>;
            };

            template <stdr::size_t N>
            using make_index_sequence = typename make_index_sequence_impl<0, N, void>::type;
#endif

            // Available since C++11, but including <utility> just for this is an overkill.
            template <class T>
            typename stdr::add_rvalue_reference<T>::type declval() noexcept;

            // Similarly, including <array> is an overkill.
            template <class T, stdr::size_t N>
            struct array {
                T data_[N];
                constexpr T operator[](stdr::size_t idx) const noexcept { return data_[idx]; }
                JKJ_CONSTEXPR14 T& operator[](stdr::size_t idx) noexcept { return data_[idx]; }
            };
        }


        ////////////////////////////////////////////////////////////////////////////////////////
        // Some basic features for encoding/decoding IEEE-754 formats.
        ////////////////////////////////////////////////////////////////////////////////////////
        namespace detail {
            template <class T>
            struct physical_bits {
                static constexpr stdr::size_t value =
                    sizeof(T) * stdr::numeric_limits<unsigned char>::digits;
            };
            template <class T>
            struct value_bits {
                static constexpr stdr::size_t value = stdr::numeric_limits<
                    typename stdr::enable_if<stdr::is_integral<T>::value, T>::type>::digits;
            };

            template <typename To, typename From>
            JKJ_CONSTEXPR20 To bit_cast(const From& from) {
#if JKJ_HAS_BIT_CAST
                return stdr::bit_cast<To>(from);
#else
                static_assert(sizeof(From) == sizeof(To), "");
                static_assert(stdr::is_trivially_copyable<To>::value, "");
                static_assert(stdr::is_trivially_copyable<From>::value, "");
                To to;
                stdr::memcpy(&to, &from, sizeof(To));
                return to;
#endif
            }
        }

        // These classes expose encoding specs of IEEE-754-like floating-point formats.
        // Currently available formats are IEEE-754 binary32 & IEEE-754 binary64.

        struct ieee754_binary32 {
            static constexpr int total_bits = 32;
            static constexpr int significand_bits = 23;
            static constexpr int exponent_bits = 8;
            static constexpr int min_exponent = -126;
            static constexpr int max_exponent = 127;
            static constexpr int exponent_bias = -127;
            static constexpr int decimal_significand_digits = 9;
            static constexpr int decimal_exponent_digits = 2;
        };
        struct ieee754_binary64 {
            static constexpr int total_bits = 64;
            static constexpr int significand_bits = 52;
            static constexpr int exponent_bits = 11;
            static constexpr int min_exponent = -1022;
            static constexpr int max_exponent = 1023;
            static constexpr int exponent_bias = -1023;
            static constexpr int decimal_significand_digits = 17;
            static constexpr int decimal_exponent_digits = 3;
        };

        // A floating-point format traits class defines ways to interpret a bit pattern of given size as
        // an encoding of floating-point number. This is an implementation of such a traits class,
        // supporting ways to interpret IEEE-754 binary floating-point numbers.
        template <class Format, class CarrierUInt, class ExponentInt = int>
        struct ieee754_binary_traits {
            // CarrierUInt needs to have enough size to hold the entire contents of floating-point
            // numbers. The actual bits are assumed to be aligned to the LSB, and every other bits are
            // assumed to be zeroed.
            static_assert(detail::value_bits<CarrierUInt>::value >= Format::total_bits,
                          "jkj::dragonbox: insufficient number of bits");
            static_assert(detail::stdr::is_unsigned<CarrierUInt>::value, "");

            // ExponentUInt needs to be large enough to hold (unsigned) exponent bits as well as the
            // (signed) actual exponent.
            // TODO: static overflow guard against intermediate computations.
            static_assert(detail::value_bits<ExponentInt>::value >= Format::exponent_bits + 1,
                          "jkj::dragonbox: insufficient number of bits");
            static_assert(detail::stdr::is_signed<ExponentInt>::value, "");

            using format = Format;
            using carrier_uint = CarrierUInt;
            static constexpr int carrier_bits = int(detail::value_bits<carrier_uint>::value);
            using exponent_int = ExponentInt;

            // Extract exponent bits from a bit pattern.
            // The result must be aligned to the LSB so that there is no additional zero paddings
            // on the right. This function does not do bias adjustment.
            static constexpr exponent_int extract_exponent_bits(carrier_uint u) noexcept {
                return exponent_int((u >> format::significand_bits) &
                                    ((exponent_int(1) << format::exponent_bits) - 1));
            }

            // Extract significand bits from a bit pattern.
            // The result must be aligned to the LSB so that there is no additional zero paddings
            // on the right. The result does not contain the implicit bit.
            static constexpr carrier_uint extract_significand_bits(carrier_uint u) noexcept {
                return carrier_uint(u & ((carrier_uint(1) << format::significand_bits) - 1u));
            }

            // Remove the exponent bits and extract significand bits together with the sign bit.
            static constexpr carrier_uint remove_exponent_bits(carrier_uint u) noexcept {
                return carrier_uint(u & ~(((carrier_uint(1) << format::exponent_bits) - 1u)
                                          << format::significand_bits));
            }

            // Shift the obtained signed significand bits to the left by 1 to remove the sign bit.
            static constexpr carrier_uint remove_sign_bit_and_shift(carrier_uint u) noexcept {
                return carrier_uint((carrier_uint(u) << 1) &
                                    ((((carrier_uint(1) << (Format::total_bits - 1)) - 1u) << 1) | 1u));
            }

            // Obtain the actual value of the binary exponent from the extracted exponent bits.
            static constexpr exponent_int binary_exponent(exponent_int exponent_bits) noexcept {
                return exponent_int(exponent_bits == 0 ? format::min_exponent
                                                       : exponent_bits + format::exponent_bias);
            }

            // Obtain the actual value of the binary significand from the extracted significand bits
            // and exponent bits.
            static constexpr carrier_uint binary_significand(carrier_uint significand_bits,
                                                             exponent_int exponent_bits) noexcept {
                return carrier_uint(
                    exponent_bits == 0
                        ? significand_bits
                        : (significand_bits | (carrier_uint(1) << format::significand_bits)));
            }

            /* Various boolean observer functions */

            static constexpr bool is_nonzero(carrier_uint u) noexcept {
                return (u & ((carrier_uint(1) << (format::significand_bits + format::exponent_bits)) -
                             1u)) != 0;
            }
            static constexpr bool is_positive(carrier_uint u) noexcept {
                return u < (carrier_uint(1) << (format::significand_bits + format::exponent_bits));
            }
            static constexpr bool is_negative(carrier_uint u) noexcept { return !is_positive(u); }
            static constexpr bool is_finite(exponent_int exponent_bits) noexcept {
                return exponent_bits != ((exponent_int(1) << format::exponent_bits) - 1);
            }
            static constexpr bool has_all_zero_significand_bits(carrier_uint u) noexcept {
                return ((u << 1) &
                        ((((carrier_uint(1) << (Format::total_bits - 1)) - 1u) << 1) | 1u)) == 0;
            }
            static constexpr bool has_even_significand_bits(carrier_uint u) noexcept {
                return u % 2 == 0;
            }
        };

        // Convert between bit patterns stored in carrier_uint and instances of an actual
        // floating-point type. Depending on format and carrier_uint, this operation might not
        // be possible for some specific bit patterns. However, the contract is that u always
        // denotes a valid bit pattern, so the functions here are assumed to be noexcept.
        // Users might specialize this class to change the behavior for certain types.
        // The default provided by the library is to treat the given floating-point type Float as either
        // IEEE-754 binary32 or IEEE-754 binary64, depending on the bitwise size of Float.
        template <class Float>
        struct default_float_bit_carrier_conversion_traits {
            // Guards against types that have different internal representations than IEEE-754
            // binary32/64. I don't know if there is a truly reliable way of detecting IEEE-754 binary
            // formats. I just did my best here. Note that in some cases
            // numeric_limits<Float>::is_iec559 may report false even if the internal representation is
            // IEEE-754 compatible. In such a case, the user can specialize this traits template and
            // remove this static sanity check in order to make Dragonbox work for Float.
            static_assert(detail::stdr::numeric_limits<Float>::is_iec559 &&
                              detail::stdr::numeric_limits<Float>::radix == 2 &&
                              (detail::physical_bits<Float>::value == 32 ||
                               detail::physical_bits<Float>::value == 64),
                          "jkj::dragonbox: Float may not be of IEEE-754 binary32/binary64");

            // Specifies the unsigned integer type to hold bitwise value of Float.
            using carrier_uint =
                typename detail::stdr::conditional<detail::physical_bits<Float>::value == 32,
                                                   detail::stdr::uint_least32_t,
                                                   detail::stdr::uint_least64_t>::type;

            // Specifies the floating-point format.
            using format = typename detail::stdr::conditional<detail::physical_bits<Float>::value == 32,
                                                              ieee754_binary32, ieee754_binary64>::type;

            // Converts the floating-point type into the bit-carrier unsigned integer type.
            static JKJ_CONSTEXPR20 carrier_uint float_to_carrier(Float x) noexcept {
                return detail::bit_cast<carrier_uint>(x);
            }

            // Converts the bit-carrier unsigned integer type into the floating-point type.
            static JKJ_CONSTEXPR20 Float carrier_to_float(carrier_uint x) noexcept {
                return detail::bit_cast<Float>(x);
            }
        };

        // Convenient wrappers for floating-point traits classes.
        // In order to reduce the argument passing overhead, these classes should be as simple as
        // possible (e.g., no inheritance, no private non-static data member, etc.; this is an
        // unfortunate fact about common ABI convention).

        template <class FormatTraits>
        struct signed_significand_bits {
            using format_traits = FormatTraits;
            using carrier_uint = typename format_traits::carrier_uint;

            carrier_uint u;

            signed_significand_bits() = default;
            constexpr explicit signed_significand_bits(carrier_uint bit_pattern) noexcept
                : u{bit_pattern} {}

            // Shift the obtained signed significand bits to the left by 1 to remove the sign bit.
            constexpr carrier_uint remove_sign_bit_and_shift() const noexcept {
                return format_traits::remove_sign_bit_and_shift(u);
            }

            constexpr bool is_positive() const noexcept { return format_traits::is_positive(u); }
            constexpr bool is_negative() const noexcept { return format_traits::is_negative(u); }
            constexpr bool has_all_zero_significand_bits() const noexcept {
                return format_traits::has_all_zero_significand_bits(u);
            }
            constexpr bool has_even_significand_bits() const noexcept {
                return format_traits::has_even_significand_bits(u);
            }
        };

        template <class FormatTraits>
        struct float_bits {
            using format_traits = FormatTraits;
            using carrier_uint = typename format_traits::carrier_uint;
            using exponent_int = typename format_traits::exponent_int;

            carrier_uint u;

            float_bits() = default;
            constexpr explicit float_bits(carrier_uint bit_pattern) noexcept : u{bit_pattern} {}

            // Extract exponent bits from a bit pattern.
            // The result must be aligned to the LSB so that there is no additional zero paddings
            // on the right. This function does not do bias adjustment.
            constexpr exponent_int extract_exponent_bits() const noexcept {
                return format_traits::extract_exponent_bits(u);
            }

            // Extract significand bits from a bit pattern.
            // The result must be aligned to the LSB so that there is no additional zero paddings
            // on the right. The result does not contain the implicit bit.
            constexpr carrier_uint extract_significand_bits() const noexcept {
                return format_traits::extract_significand_bits(u);
            }

            // Remove the exponent bits and extract significand bits together with the sign bit.
            constexpr signed_significand_bits<format_traits> remove_exponent_bits() const noexcept {
                return signed_significand_bits<format_traits>(format_traits::remove_exponent_bits(u));
            }

            // Obtain the actual value of the binary exponent from the extracted exponent bits.
            static constexpr exponent_int binary_exponent(exponent_int exponent_bits) noexcept {
                return format_traits::binary_exponent(exponent_bits);
            }
            constexpr exponent_int binary_exponent() const noexcept {
                return binary_exponent(extract_exponent_bits());
            }

            // Obtain the actual value of the binary exponent from the extracted significand bits
            // and exponent bits.
            static constexpr carrier_uint binary_significand(carrier_uint significand_bits,
                                                             exponent_int exponent_bits) noexcept {
                return format_traits::binary_significand(significand_bits, exponent_bits);
            }
            constexpr carrier_uint binary_significand() const noexcept {
                return binary_significand(extract_significand_bits(), extract_exponent_bits());
            }

            constexpr bool is_nonzero() const noexcept { return format_traits::is_nonzero(u); }
            constexpr bool is_positive() const noexcept { return format_traits::is_positive(u); }
            constexpr bool is_negative() const noexcept { return format_traits::is_negative(u); }
            constexpr bool is_finite(exponent_int exponent_bits) const noexcept {
                return format_traits::is_finite(exponent_bits);
            }
            constexpr bool is_finite() const noexcept {
                return format_traits::is_finite(extract_exponent_bits());
            }
            constexpr bool has_even_significand_bits() const noexcept {
                return format_traits::has_even_significand_bits(u);
            }
        };

        template <class Float,
                  class ConversionTraits = default_float_bit_carrier_conversion_traits<Float>,
                  class FormatTraits = ieee754_binary_traits<typename ConversionTraits::format,
                                                             typename ConversionTraits::carrier_uint>>
        JKJ_CONSTEXPR20 float_bits<FormatTraits> make_float_bits(Float x) noexcept {
            return float_bits<FormatTraits>(ConversionTraits::float_to_carrier(x));
        }

        namespace detail {
            ////////////////////////////////////////////////////////////////////////////////////////
            // Bit operation intrinsics.
            ////////////////////////////////////////////////////////////////////////////////////////

            namespace bits {
                // Most compilers should be able to optimize this into the ROR instruction.
                // n is assumed to be at most of bit_width bits.
                template <stdr::size_t bit_width, class UInt>
                JKJ_CONSTEXPR14 UInt rotr(UInt n, unsigned int r) noexcept {
                    static_assert(bit_width > 0, "jkj::dragonbox: rotation bit-width must be positive");
                    static_assert(bit_width <= value_bits<UInt>::value,
                                  "jkj::dragonbox: rotation bit-width is too large");
                    r &= (bit_width - 1);
                    return (n >> r) | (n << ((bit_width - r) & (bit_width - 1)));
                }
            }

            ////////////////////////////////////////////////////////////////////////////////////////
            // Utilities for wide unsigned integer arithmetic.
            ////////////////////////////////////////////////////////////////////////////////////////

            namespace wuint {
                // Compilers might support built-in 128-bit integer types. However, it seems that
                // emulating them with a pair of 64-bit integers actually produces a better code,
                // so we avoid using those built-ins. That said, they are still useful for
                // implementing 64-bit x 64-bit -> 128-bit multiplication.

                // clang-format off
#if defined(__SIZEOF_INT128__)
		// To silence "error: ISO C++ does not support '__int128' for 'type name'
		// [-Wpedantic]"
#if defined(__GNUC__)
			__extension__
#endif
			using builtin_uint128_t = unsigned __int128;
#endif
                // clang-format on

                struct uint128 {
                    uint128() = default;

                    stdr::uint_least64_t high_;
                    stdr::uint_least64_t low_;

                    constexpr uint128(stdr::uint_least64_t high, stdr::uint_least64_t low) noexcept
                        : high_{high}, low_{low} {}

                    constexpr stdr::uint_least64_t high() const noexcept { return high_; }
                    constexpr stdr::uint_least64_t low() const noexcept { return low_; }

                    JKJ_CONSTEXPR20 uint128& operator+=(stdr::uint_least64_t n) & noexcept {
                        auto const generic_impl = [&] {
                            auto const sum = (low_ + n) & UINT64_C(0xffffffffffffffff);
                            high_ += (sum < low_ ? 1 : 0);
                            low_ = sum;
                        };
                        // To suppress warning.
                        static_cast<void>(generic_impl);

                        JKJ_IF_CONSTEXPR(value_bits<stdr::uint_least64_t>::value > 64) {
                            generic_impl();
                            return *this;
                        }

                        JKJ_IF_CONSTEVAL {
                            generic_impl();
                            return *this;
                        }

                        // See https://github.com/fmtlib/fmt/pull/2985.
#if JKJ_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
                        JKJ_IF_CONSTEXPR(
                            stdr::is_same<stdr::uint_least64_t, unsigned long long>::value) {
                            unsigned long long carry{};
                            low_ = stdr::uint_least64_t(__builtin_addcll(low_, n, 0, &carry));
                            high_ = stdr::uint_least64_t(__builtin_addcll(high_, 0, carry, &carry));
                            return *this;
                        }
#endif
#if JKJ_HAS_BUILTIN(__builtin_addcl) && !defined(__ibmxl__)
                        JKJ_IF_CONSTEXPR(stdr::is_same<stdr::uint_least64_t, unsigned long>::value) {
                            unsigned long carry{};
                            low_ = stdr::uint_least64_t(
                                __builtin_addcl(static_cast<unsigned long>(low_),
                                                static_cast<unsigned long>(n), 0, &carry));
                            high_ = stdr::uint_least64_t(
                                __builtin_addcl(static_cast<unsigned long>(high_), 0, carry, &carry));
                            return *this;
                        }
#endif
#if JKJ_HAS_BUILTIN(__builtin_addc) && !defined(__ibmxl__)
                        JKJ_IF_CONSTEXPR(stdr::is_same<stdr::uint_least64_t, unsigned int>::value) {
                            unsigned int carry{};
                            low_ = stdr::uint_least64_t(__builtin_addc(static_cast<unsigned int>(low_),
                                                                       static_cast<unsigned int>(n), 0,
                                                                       &carry));
                            high_ = stdr::uint_least64_t(
                                __builtin_addc(static_cast<unsigned int>(high_), 0, carry, &carry));
                            return *this;
                        }
#endif

#if JKJ_HAS_BUILTIN(__builtin_ia32_addcarry_u64)
                        // __builtin_ia32_addcarry_u64 is not documented, but it seems it takes unsigned
                        // long long arguments.
                        unsigned long long result{};
                        auto const carry = __builtin_ia32_addcarry_u64(0, low_, n, &result);
                        low_ = stdr::uint_least64_t(result);
                        __builtin_ia32_addcarry_u64(carry, high_, 0, &result);
                        high_ = stdr::uint_least64_t(result);
#elif defined(_MSC_VER) && defined(_M_X64)
                        // On MSVC, uint_least64_t and __int64 must be unsigned long long; see
                        // https://learn.microsoft.com/en-us/cpp/c-runtime-library/standard-types
                        // and https://learn.microsoft.com/en-us/cpp/cpp/int8-int16-int32-int64.
                        static_assert(stdr::is_same<unsigned long long, stdr::uint_least64_t>::value,
                                      "");
                        auto const carry = _addcarry_u64(0, low_, n, &low_);
                        _addcarry_u64(carry, high_, 0, &high_);
#elif defined(__INTEL_COMPILER) && (defined(_M_X64) || defined(__x86_64))
                        // Cannot find any documentation on how things are defined, but hopefully this
                        // is always true...
                        static_assert(stdr::is_same<unsigned __int64, stdr::uint_least64_t>::value, "");
                        auto const carry = _addcarry_u64(0, low_, n, &low_);
                        _addcarry_u64(carry, high_, 0, &high_);
#else
                        generic_impl();
#endif
                        return *this;
                    }
                };

                inline JKJ_CONSTEXPR20 stdr::uint_least64_t umul64(stdr::uint_least32_t x,
                                                                   stdr::uint_least32_t y) noexcept {
#if defined(_MSC_VER) && defined(_M_IX86)
                    JKJ_IF_NOT_CONSTEVAL { return __emulu(x, y); }
#endif
                    return x * stdr::uint_least64_t(y);
                }

                // Get 128-bit result of multiplication of two 64-bit unsigned integers.
                JKJ_SAFEBUFFERS inline JKJ_CONSTEXPR20 uint128
                umul128(stdr::uint_least64_t x, stdr::uint_least64_t y) noexcept {
                    auto const generic_impl = [=]() -> uint128 {
                        auto const a = stdr::uint_least32_t(x >> 32);
                        auto const b = stdr::uint_least32_t(x);
                        auto const c = stdr::uint_least32_t(y >> 32);
                        auto const d = stdr::uint_least32_t(y);

                        auto const ac = umul64(a, c);
                        auto const bc = umul64(b, c);
                        auto const ad = umul64(a, d);
                        auto const bd = umul64(b, d);

                        auto const intermediate =
                            (bd >> 32) + stdr::uint_least32_t(ad) + stdr::uint_least32_t(bc);

                        return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
                                (intermediate << 32) + stdr::uint_least32_t(bd)};
                    };
                    // To silence warning.
                    static_cast<void>(generic_impl);

#if defined(__SIZEOF_INT128__)
                    auto const result = builtin_uint128_t(x) * builtin_uint128_t(y);
                    return {stdr::uint_least64_t(result >> 64), stdr::uint_least64_t(result)};
#elif defined(_MSC_VER) && defined(_M_X64)
                    JKJ_IF_CONSTEVAL {
                        // This redundant variable is to workaround MSVC's codegen bug caused by the
                        // interaction of NRVO and intrinsics.
                        auto const result = generic_impl();
                        return result;
                    }
                    uint128 result;
    #if defined(__AVX2__)
                    result.low_ = _mulx_u64(x, y, &result.high_);
    #else
                    result.low_ = _umul128(x, y, &result.high_);
    #endif
                    return result;
#else
                    return generic_impl();
#endif
                }

                // Get high half of the 128-bit result of multiplication of two 64-bit unsigned
                // integers.
                JKJ_SAFEBUFFERS inline JKJ_CONSTEXPR20 stdr::uint_least64_t
                umul128_upper64(stdr::uint_least64_t x, stdr::uint_least64_t y) noexcept {
                    auto const generic_impl = [=]() -> stdr::uint_least64_t {
                        auto const a = stdr::uint_least32_t(x >> 32);
                        auto const b = stdr::uint_least32_t(x);
                        auto const c = stdr::uint_least32_t(y >> 32);
                        auto const d = stdr::uint_least32_t(y);

                        auto const ac = umul64(a, c);
                        auto const bc = umul64(b, c);
                        auto const ad = umul64(a, d);
                        auto const bd = umul64(b, d);

                        auto const intermediate =
                            (bd >> 32) + stdr::uint_least32_t(ad) + stdr::uint_least32_t(bc);

                        return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32);
                    };
                    // To silence warning.
                    static_cast<void>(generic_impl);

#if defined(__SIZEOF_INT128__)
                    auto const result = builtin_uint128_t(x) * builtin_uint128_t(y);
                    return stdr::uint_least64_t(result >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
                    JKJ_IF_CONSTEVAL {
                        // This redundant variable is to workaround MSVC's codegen bug caused by the
                        // interaction of NRVO and intrinsics.
                        auto const result = generic_impl();
                        return result;
                    }
                    stdr::uint_least64_t result;
    #if defined(__AVX2__)
                    _mulx_u64(x, y, &result);
    #else
                    result = __umulh(x, y);
    #endif
                    return result;
#else
                    return generic_impl();
#endif
                }

                // Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
                // unsigned integer.
                JKJ_SAFEBUFFERS inline JKJ_CONSTEXPR20 uint128 umul192_upper128(stdr::uint_least64_t x,
                                                                                uint128 y) noexcept {
                    auto r = umul128(x, y.high());
                    r += umul128_upper64(x, y.low());
                    return r;
                }

                // Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
                // unsigned integer.
                inline JKJ_CONSTEXPR20 stdr::uint_least64_t
                umul96_upper64(stdr::uint_least32_t x, stdr::uint_least64_t y) noexcept {
#if defined(__SIZEOF_INT128__) || (defined(_MSC_VER) && defined(_M_X64))
                    return umul128_upper64(stdr::uint_least64_t(x) << 32, y);
#else
                    auto const yh = stdr::uint_least32_t(y >> 32);
                    auto const yl = stdr::uint_least32_t(y);

                    auto const xyh = umul64(x, yh);
                    auto const xyl = umul64(x, yl);

                    return xyh + (xyl >> 32);
#endif
                }

                // Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
                // unsigned integer.
                JKJ_SAFEBUFFERS inline JKJ_CONSTEXPR20 uint128 umul192_lower128(stdr::uint_least64_t x,
                                                                                uint128 y) noexcept {
                    auto const high = x * y.high();
                    auto const high_low = umul128(x, y.low());
                    return {(high + high_low.high()) & UINT64_C(0xffffffffffffffff), high_low.low()};
                }

                // Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
                // unsigned integer.
                constexpr stdr::uint_least64_t umul96_lower64(stdr::uint_least32_t x,
                                                              stdr::uint_least64_t y) noexcept {
                    return (x * y) & UINT64_C(0xffffffffffffffff);
                }
            }

            ////////////////////////////////////////////////////////////////////////////////////////
            // Some simple utilities for constexpr computation.
            ////////////////////////////////////////////////////////////////////////////////////////

            template <int k, class Int>
            constexpr Int compute_power(Int a) noexcept {
                static_assert(k >= 0, "");
#if JKJ_HAS_CONSTEXPR14
                Int p = 1;
                for (int i = 0; i < k; ++i) {
                    p *= a;
                }
                return p;
#else
                return k == 0       ? 1
                       : k % 2 == 0 ? compute_power<k / 2, Int>(a * a)
                                    : a * compute_power<k / 2, Int>(a * a);
#endif
            }

            template <int a, class UInt>
            constexpr int count_factors(UInt n) noexcept {
                static_assert(a > 1, "");
#if JKJ_HAS_CONSTEXPR14
                int c = 0;
                while (n % a == 0) {
                    n /= a;
                    ++c;
                }
                return c;
#else
                return n % a == 0 ? count_factors<a, UInt>(n / a) + 1 : 0;
#endif
            }

            ////////////////////////////////////////////////////////////////////////////////////////
            // Utilities for fast/constexpr log computation.
            ////////////////////////////////////////////////////////////////////////////////////////

            namespace log {
                static_assert((stdr::int_fast32_t(-1) >> 1) == stdr::int_fast32_t(-1) &&
                                  (stdr::int_fast16_t(-1) >> 1) == stdr::int_fast16_t(-1),
                              "jkj::dragonbox: right-shift for signed integers must be arithmetic");

                // For constexpr computation.
                // Returns -1 when n = 0.
                template <class UInt>
                constexpr int floor_log2(UInt n) noexcept {
#if JKJ_HAS_CONSTEXPR14
                    int count = -1;
                    while (n != 0) {
                        ++count;
                        n >>= 1;
                    }
                    return count;
#else
                    return n == 0 ? -1 : floor_log2<UInt>(n / 2) + 1;
#endif
                }

                template <template <stdr::size_t> class Info, stdr::int_least32_t min_exponent,
                          stdr::int_least32_t max_exponent, stdr::size_t current_tier,
                          stdr::int_least32_t supported_min_exponent = Info<current_tier>::min_exponent,
                          stdr::int_least32_t supported_max_exponent = Info<current_tier>::max_exponent>
                constexpr bool is_in_range(int) noexcept {
                    return min_exponent >= supported_min_exponent &&
                           max_exponent <= supported_max_exponent;
                }
                template <template <stdr::size_t> class Info, stdr::int_least32_t min_exponent,
                          stdr::int_least32_t max_exponent, stdr::size_t current_tier>
                constexpr bool is_in_range(...) noexcept {
                    // Supposed to be always false, but formally dependent on the template parameters.
                    static_assert(min_exponent > max_exponent,
                                  "jkj::dragonbox: exponent range is too wide");
                    return false;
                }

                template <template <stdr::size_t> class Info, stdr::int_least32_t min_exponent,
                          stdr::int_least32_t max_exponent, stdr::size_t current_tier = 0,
                          bool = is_in_range<Info, min_exponent, max_exponent, current_tier>(0)>
                struct compute_impl;

                template <template <stdr::size_t> class Info, stdr::int_least32_t min_exponent,
                          stdr::int_least32_t max_exponent, stdr::size_t current_tier>
                struct compute_impl<Info, min_exponent, max_exponent, current_tier, true> {
                    using info = Info<current_tier>;
                    using default_return_type = typename info::default_return_type;
                    template <class ReturnType, class Int>
                    static constexpr ReturnType compute(Int e) noexcept {
#if JKJ_HAS_CONSTEXPR14
                        assert(min_exponent <= e && e <= max_exponent);
#endif
                        // The sign is irrelevant for the mathematical validity of the formula, but
                        // assuming positivity makes the overflow analysis simpler.
                        static_assert(info::multiply >= 0 && info::subtract >= 0, "");
                        return static_cast<ReturnType>((e * info::multiply - info::subtract) >>
                                                       info::shift);
                    }
                };

                template <template <stdr::size_t> class Info, stdr::int_least32_t min_exponent,
                          stdr::int_least32_t max_exponent, stdr::size_t current_tier>
                struct compute_impl<Info, min_exponent, max_exponent, current_tier, false> {
                    using next_tier = compute_impl<Info, min_exponent, max_exponent, current_tier + 1>;
                    using default_return_type = typename next_tier::default_return_type;
                    template <class ReturnType, class Int>
                    static constexpr ReturnType compute(Int e) noexcept {
                        return next_tier::template compute<ReturnType>(e);
                    }
                };

                template <stdr::size_t tier>
                struct floor_log10_pow2_info;
                template <>
                struct floor_log10_pow2_info<0> {
                    using default_return_type = stdr::int_fast8_t;
                    static constexpr stdr::int_fast16_t multiply = 77;
                    static constexpr stdr::int_fast16_t subtract = 0;
                    static constexpr stdr::size_t shift = 8;
                    static constexpr stdr::int_least32_t min_exponent = -102;
                    static constexpr stdr::int_least32_t max_exponent = 102;
                };
                template <>
                struct floor_log10_pow2_info<1> {
                    using default_return_type = stdr::int_fast8_t;
                    // 24-bits are enough in fact.
                    static constexpr stdr::int_fast32_t multiply = 1233;
                    static constexpr stdr::int_fast32_t subtract = 0;
                    static constexpr stdr::size_t shift = 12;
                    // Formula itself holds on [-680,680]; [-425,425] is to ensure that the output is
                    // within [-127,127].
                    static constexpr stdr::int_least32_t min_exponent = -425;
                    static constexpr stdr::int_least32_t max_exponent = 425;
                };
                template <>
                struct floor_log10_pow2_info<2> {
                    using default_return_type = stdr::int_fast16_t;
                    static constexpr stdr::int_fast32_t multiply = INT32_C(315653);
                    static constexpr stdr::int_fast32_t subtract = 0;
                    static constexpr stdr::size_t shift = 20;
                    static constexpr stdr::int_least32_t min_exponent = -2620;
                    static constexpr stdr::int_least32_t max_exponent = 2620;
                };
                template <stdr::int_least32_t min_exponent = -2620,
                          stdr::int_least32_t max_exponent = 2620,
                          class ReturnType = typename compute_impl<floor_log10_pow2_info, min_exponent,
                                                                   max_exponent>::default_return_type,
                          class Int>
                constexpr ReturnType floor_log10_pow2(Int e) noexcept {
                    return compute_impl<floor_log10_pow2_info, min_exponent,
                                        max_exponent>::template compute<ReturnType>(e);
                }

                template <stdr::size_t tier>
                struct floor_log2_pow10_info;
                template <>
                struct floor_log2_pow10_info<0> {
                    using default_return_type = stdr::int_fast8_t;
                    static constexpr stdr::int_fast16_t multiply = 53;
                    static constexpr stdr::int_fast16_t subtract = 0;
                    static constexpr stdr::size_t shift = 4;
                    static constexpr stdr::int_least32_t min_exponent = -15;
                    static constexpr stdr::int_least32_t max_exponent = 18;
                };
                template <>
                struct floor_log2_pow10_info<1> {
                    using default_return_type = stdr::int_fast16_t;
                    // 24-bits are enough in fact.
                    static constexpr stdr::int_fast32_t multiply = 1701;
                    static constexpr stdr::int_fast32_t subtract = 0;
                    static constexpr stdr::size_t shift = 9;
                    static constexpr stdr::int_least32_t min_exponent = -58;
                    static constexpr stdr::int_least32_t max_exponent = 58;
                };
                template <>
                struct floor_log2_pow10_info<2> {
                    using default_return_type = stdr::int_fast16_t;
                    static constexpr stdr::int_fast32_t multiply = INT32_C(1741647);
                    static constexpr stdr::int_fast32_t subtract = 0;
                    static constexpr stdr::size_t shift = 19;
                    // Formula itself holds on [-4003,4003]; [-1233,1233] is to ensure no overflow.
                    static constexpr stdr::int_least32_t min_exponent = -1233;
                    static constexpr stdr::int_least32_t max_exponent = 1233;
                };
                template <stdr::int_least32_t min_exponent = -1233,
                          stdr::int_least32_t max_exponent = 1233,
                          class ReturnType = typename compute_impl<floor_log2_pow10_info, min_exponent,
                                                                   max_exponent>::default_return_type,
                          class Int>
                constexpr ReturnType floor_log2_pow10(Int e) noexcept {
                    return compute_impl<floor_log2_pow10_info, min_exponent,
                                        max_exponent>::template compute<ReturnType>(e);
                }

                template <stdr::size_t tier>
                struct floor_log10_pow2_minus_log10_4_over_3_info;
                template <>
                struct floor_log10_pow2_minus_log10_4_over_3_info<0> {
                    using default_return_type = stdr::int_fast8_t;
                    static constexpr stdr::int_fast16_t multiply = 77;
                    static constexpr stdr::int_fast16_t subtract = 31;
                    static constexpr stdr::size_t shift = 8;
                    static constexpr stdr::int_least32_t min_exponent = -75;
                    static constexpr stdr::int_least32_t max_exponent = 129;
                };
                template <>
                struct floor_log10_pow2_minus_log10_4_over_3_info<1> {
                    using default_return_type = stdr::int_fast8_t;
                    // 24-bits are enough in fact.
                    static constexpr stdr::int_fast32_t multiply = 19728;
                    static constexpr stdr::int_fast32_t subtract = 8241;
                    static constexpr stdr::size_t shift = 16;
                    // Formula itself holds on [-849,315]; [-424,315] is to ensure that the output is
                    // within [-127,127].
                    static constexpr stdr::int_least32_t min_exponent = -424;
                    static constexpr stdr::int_least32_t max_exponent = 315;
                };
                template <>
                struct floor_log10_pow2_minus_log10_4_over_3_info<2> {
                    using default_return_type = stdr::int_fast16_t;
                    static constexpr stdr::int_fast32_t multiply = INT32_C(631305);
                    static constexpr stdr::int_fast32_t subtract = INT32_C(261663);
                    static constexpr stdr::size_t shift = 21;
                    static constexpr stdr::int_least32_t min_exponent = -2985;
                    static constexpr stdr::int_least32_t max_exponent = 2936;
                };
                template <stdr::int_least32_t min_exponent = -2985,
                          stdr::int_least32_t max_exponent = 2936,
                          class ReturnType =
                              typename compute_impl<floor_log10_pow2_minus_log10_4_over_3_info,
                                                    min_exponent, max_exponent>::default_return_type,
                          class Int>
                constexpr ReturnType floor_log10_pow2_minus_log10_4_over_3(Int e) noexcept {
                    return compute_impl<floor_log10_pow2_minus_log10_4_over_3_info, min_exponent,
                                        max_exponent>::template compute<ReturnType>(e);
                }

                template <stdr::size_t tier>
                struct floor_log5_pow2_info;
                template <>
                struct floor_log5_pow2_info<0> {
                    using default_return_type = stdr::int_fast32_t;
                    static constexpr stdr::int_fast32_t multiply = INT32_C(225799);
                    static constexpr stdr::int_fast32_t subtract = 0;
                    static constexpr stdr::size_t shift = 19;
                    static constexpr stdr::int_least32_t min_exponent = -1831;
                    static constexpr stdr::int_least32_t max_exponent = 1831;
                };
                template <stdr::int_least32_t min_exponent = -1831,
                          stdr::int_least32_t max_exponent = 1831,
                          class ReturnType = typename compute_impl<floor_log5_pow2_info, min_exponent,
                                                                   max_exponent>::default_return_type,
                          class Int>
                constexpr ReturnType floor_log5_pow2(Int e) noexcept {
                    return compute_impl<floor_log5_pow2_info, min_exponent,
                                        max_exponent>::template compute<ReturnType>(e);
                }

                template <stdr::size_t tier>
                struct floor_log5_pow2_minus_log5_3_info;
                template <>
                struct floor_log5_pow2_minus_log5_3_info<0> {
                    using default_return_type = stdr::int_fast32_t;
                    static constexpr stdr::int_fast32_t multiply = INT32_C(451597);
                    static constexpr stdr::int_fast32_t subtract = INT32_C(715764);
                    static constexpr stdr::size_t shift = 20;
                    static constexpr stdr::int_least32_t min_exponent = -3543;
                    static constexpr stdr::int_least32_t max_exponent = 2427;
                };
                template <stdr::int_least32_t min_exponent = -3543,
                          stdr::int_least32_t max_exponent = 2427,
                          class ReturnType =
                              typename compute_impl<floor_log5_pow2_minus_log5_3_info, min_exponent,
                                                    max_exponent>::default_return_type,
                          class Int>
                constexpr ReturnType floor_log5_pow2_minus_log5_3(Int e) noexcept {
                    return compute_impl<floor_log5_pow2_minus_log5_3_info, min_exponent,
                                        max_exponent>::template compute<ReturnType>(e);
                }
            }

            ////////////////////////////////////////////////////////////////////////////////////////
            // Utilities for fast divisibility tests.
            ////////////////////////////////////////////////////////////////////////////////////////

            namespace div {
                // Replace n by floor(n / 10^N).
                // Returns true if and only if n is divisible by 10^N.
                // Precondition: n <= 10^(N+1)
                // !!It takes an in-out parameter!!
                template <int N, class UInt>
                struct divide_by_pow10_info;

                template <class UInt>
                struct divide_by_pow10_info<1, UInt> {
                    static constexpr stdr::uint_fast32_t magic_number = 6554;
                    static constexpr int shift_amount = 16;
                };

                template <>
                struct divide_by_pow10_info<1, stdr::uint_least8_t> {
                    static constexpr stdr::uint_fast16_t magic_number = 103;
                    static constexpr int shift_amount = 10;
                };

                template <>
                struct divide_by_pow10_info<1, stdr::uint_least16_t> {
                    static constexpr stdr::uint_fast16_t magic_number = 103;
                    static constexpr int shift_amount = 10;
                };

                template <class UInt>
                struct divide_by_pow10_info<2, UInt> {
                    static constexpr stdr::uint_fast32_t magic_number = 656;
                    static constexpr int shift_amount = 16;
                };

                template <>
                struct divide_by_pow10_info<2, stdr::uint_least16_t> {
                    static constexpr stdr::uint_fast32_t magic_number = 41;
                    static constexpr int shift_amount = 12;
                };

                template <int N, class UInt>
                JKJ_CONSTEXPR14 bool check_divisibility_and_divide_by_pow10(UInt& n) noexcept {
                    // Make sure the computation for max_n does not overflow.
                    static_assert(N + 1 <= log::floor_log10_pow2(int(value_bits<UInt>::value)), "");
                    assert(n <= compute_power<N + 1>(UInt(10)));

                    using info = divide_by_pow10_info<N, UInt>;
                    using intermediate_type = decltype(info::magic_number);
                    auto const prod = intermediate_type(n * info::magic_number);

                    constexpr auto mask =
                        intermediate_type((intermediate_type(1) << info::shift_amount) - 1);
                    bool const result = ((prod & mask) < info::magic_number);

                    n = UInt(prod >> info::shift_amount);
                    return result;
                }

                // Compute floor(n / 10^N) for small n and N.
                // Precondition: n <= 10^(N+1)
                template <int N, class UInt>
                JKJ_CONSTEXPR14 UInt small_division_by_pow10(UInt n) noexcept {
                    // Make sure the computation for max_n does not overflow.
                    static_assert(N + 1 <= log::floor_log10_pow2(int(value_bits<UInt>::value)), "");
                    assert(n <= compute_power<N + 1>(UInt(10)));

                    return UInt((n * divide_by_pow10_info<N, UInt>::magic_number) >>
                                divide_by_pow10_info<N, UInt>::shift_amount);
                }

                // Compute floor(n / 10^N) for small N.
                // Precondition: n <= n_max
                template <int N, class UInt, UInt n_max>
                JKJ_CONSTEXPR20 UInt divide_by_pow10(UInt n) noexcept {
                    static_assert(N >= 0, "");

                    // Specialize for 32-bit division by 10.
                    // Without the bound on n_max (which compilers these days never leverage), the
                    // minimum needed amount of shift is larger than 32. Hence, this may generate better
                    // code for 32-bit or smaller architectures. Even for 64-bit architectures, it seems
                    // compilers tend to generate mov + mul instead of a single imul for an unknown
                    // reason if we just write n / 10.
                    JKJ_IF_CONSTEXPR(stdr::is_same<UInt, stdr::uint_least32_t>::value && N == 1 &&
                                     n_max <= UINT32_C(1073741828)) {
                        return UInt(wuint::umul64(n, UINT32_C(429496730)) >> 32);
                    }
                    // Specialize for 64-bit division by 10.
                    // Without the bound on n_max (which compilers these days never leverage), the
                    // minimum needed amount of shift is larger than 64.
                    else JKJ_IF_CONSTEXPR(stdr::is_same<UInt, stdr::uint_least64_t>::value && N == 1 &&
                                          n_max <= UINT64_C(4611686018427387908)) {
                        return UInt(wuint::umul128_upper64(n, UINT64_C(1844674407370955162)));
                    }
                    // Specialize for 32-bit division by 100.
                    // It seems compilers tend to generate mov + mul instead of a single imul for an
                    // unknown reason if we just write n / 100.
                    else JKJ_IF_CONSTEXPR(stdr::is_same<UInt, stdr::uint_least32_t>::value && N == 2) {
                        return UInt(wuint::umul64(n, UINT32_C(1374389535)) >> 37);
                    }
                    // Specialize for 64-bit division by 1000.
                    // Without the bound on n_max (which compilers these days never leverage), the
                    // smallest magic number for this computation does not fit into 64-bits.
                    else JKJ_IF_CONSTEXPR(stdr::is_same<UInt, stdr::uint_least64_t>::value && N == 3 &&
                                          n_max <= UINT64_C(15534100272597517998)) {
                        return UInt(wuint::umul128_upper64(n, UINT64_C(4722366482869645214)) >> 8);
                    }
                    else {
                        constexpr auto divisor = compute_power<N>(UInt(10));
                        return n / divisor;
                    }
                }
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////
        // Return types for the main interface function.
        ////////////////////////////////////////////////////////////////////////////////////////

        template <class SignificandType, class ExponentType, bool is_signed, bool trailing_zero_flag>
        struct decimal_fp;

        template <class SignificandType, class ExponentType>
        struct decimal_fp<SignificandType, ExponentType, false, false> {
            SignificandType significand;
            ExponentType exponent;
        };

        template <class SignificandType, class ExponentType>
        struct decimal_fp<SignificandType, ExponentType, true, false> {
            SignificandType significand;
            ExponentType exponent;
            bool is_negative;
        };

        template <class SignificandType, class ExponentType>
        struct decimal_fp<SignificandType, ExponentType, false, true> {
            SignificandType significand;
            ExponentType exponent;
            bool may_have_trailing_zeros;
        };

        template <class SignificandType, class ExponentType>
        struct decimal_fp<SignificandType, ExponentType, true, true> {
            SignificandType significand;
            ExponentType exponent;
            bool may_have_trailing_zeros;
            bool is_negative;