From 3daf7bc899c4ccdc379ac36b72fe9ab50b4bf5cd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 26 May 2023 10:47:23 +0200 Subject: [PATCH] Port float common --- include/boost/charconv/detail/config.hpp | 31 +- .../detail/fast_float/float_common.hpp | 511 ++++++++++++++++++ 2 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 include/boost/charconv/detail/fast_float/float_common.hpp diff --git a/include/boost/charconv/detail/config.hpp b/include/boost/charconv/detail/config.hpp index 283f798..26fb61f 100644 --- a/include/boost/charconv/detail/config.hpp +++ b/include/boost/charconv/detail/config.hpp @@ -7,6 +7,7 @@ #include #include +#include // Once library is complete remove this block, and Boost.Assert from the CML if still unused. #ifndef BOOST_CHARCONV_STANDALONE @@ -19,6 +20,9 @@ # define BOOST_CHARCONV_ASSERT_MSG(expr, msg) assert((expr)&&(msg)) #endif +// Rust style try macro +#define BOOST_CHARCONV_TRY(x) { if(!x) return false; } + // Use 128 bit integers and supress warnings for using extensions #if defined(BOOST_HAS_INT128) # define BOOST_CHARCONV_HAS_INT128 @@ -158,7 +162,32 @@ static_assert((BOOST_CHARCONV_ENDIAN_BIG_BYTE || BOOST_CHARCONV_ENDIAN_LITTLE_BY #if !defined(BOOST_CHARCONV_NO_CONSTEXPR_DETECTION) && __cpp_lib_constexpr_algorithms >= 201806L # define BOOST_CHARCONV_CXX20_CONSTEXPR constexpr #else -# define BOOST_CHARCONV_CXX20_CONSTEXPR inline +# define BOOST_CHARCONV_CXX20_CONSTEXPR +#endif + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__) \ + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) ) + #define BOOST_CHARCONV_FASTFLOAT_64BIT 1 +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \ + || defined(__MINGW32__) || defined(__EMSCRIPTEN__)) + #define BOOST_CHARCONV_FASTFLOAT_32BIT 1 +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. + // We can never tell the register width, but the SIZE_MAX is a good approximation. + // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. + #if SIZE_MAX == 0xffff + #error Unknown platform (16-bit, unsupported) + #elif SIZE_MAX == 0xffffffff + #define BOOST_CHARCONV_FASTFLOAT_32BIT 1 + #elif SIZE_MAX == 0xffffffffffffffff + #define BOOST_CHARCONV_FASTFLOAT_64BIT 1 + #else + #error Unknown platform (not 32-bit, not 64-bit?) + #endif #endif #endif // BOOST_CHARCONV_DETAIL_CONFIG_HPP diff --git a/include/boost/charconv/detail/fast_float/float_common.hpp b/include/boost/charconv/detail/fast_float/float_common.hpp new file mode 100644 index 0000000..6ff3895 --- /dev/null +++ b/include/boost/charconv/detail/fast_float/float_common.hpp @@ -0,0 +1,511 @@ +// Copyright 2020-2023 Daniel Lemire +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// Derivative of: https://github.com/fastfloat/fast_float + +#ifndef BOOST_CHARCONV_DETAIL_FAST_FLOAT_FLOAT_COMMON_HPP +#define BOOST_CHARCONV_DETAIL_FAST_FLOAT_FLOAT_COMMON_HPP + +#include +#include +#include +#include +#include +#include + +#ifdef __has_include +# if __has_include() +# include +# if __cpp_lib_bit_cast >= 201806L +# define BOOST_CHARCONV_HAS_STD_BITCAST +# endif +# endif +#endif + +#ifndef BOOST_CHARCONV_HAS_STD_BITCAST +# include +#endif + +namespace boost { namespace charconv { namespace detail { namespace fast_float { + +template +struct parse_options_t +{ + constexpr explicit parse_options_t(chars_format fmt = chars_format::general, UC dot = static_cast('.')) + : format(fmt), decimal_point(dot) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + UC decimal_point; +}; +using parse_options = parse_options_t; + +template +BOOST_CHARCONV_CXX14_CONSTEXPR bool fastfloat_strncasecmp(UC const * input1, UC const * input2, std::size_t length) +{ + char running_diff {0}; + for (std::size_t i = 0; i < length; ++i) + { + running_diff |= (static_cast(input1[i]) ^ static_cast(input2[i])); + } + + return (running_diff == 0) || (running_diff == 32); +} + +// a pointer and a length to a contiguous block of memory +template +struct span +{ + const T* ptr; + std::size_t length; + + constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} + constexpr span() : ptr(nullptr), length(0) {} + + constexpr std::size_t len() const noexcept + { + return length; + } + + BOOST_CHARCONV_CXX14_CONSTEXPR const T& operator[](std::size_t index) const noexcept + { + BOOST_CHARCONV_ASSERT(index < length); + return ptr[index]; + } +}; + +// slow emulation routine for 32-bit +BOOST_FORCEINLINE constexpr std::uint64_t emulu(std::uint32_t x, std::uint32_t y) +{ + return x * static_cast(y); +} + +struct adjusted_mantissa +{ + std::uint64_t mantissa{0}; + std::int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + + constexpr bool operator==(const adjusted_mantissa &o) const + { + return mantissa == o.mantissa && power2 == o.power2; + } + constexpr bool operator!=(const adjusted_mantissa &o) const + { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +static constexpr std::int32_t invalid_am_bias = -0x8000; + +// used for binary_format_lookup_tables::max_mantissa +constexpr std::uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; + +template +struct binary_format_lookup_tables; + +template +struct binary_format : binary_format_lookup_tables +{ + using equiv_uint = typename std::conditional::type; + + static inline constexpr int mantissa_explicit_bits(); + static inline constexpr int minimum_exponent(); + static inline constexpr int infinite_power(); + static inline constexpr int sign_index(); + static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST + static inline constexpr int max_exponent_fast_path(); + static inline constexpr int max_exponent_round_to_even(); + static inline constexpr int min_exponent_round_to_even(); + static inline constexpr std::uint64_t max_mantissa_fast_path(std::int64_t power); + static inline constexpr std::uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST + static inline constexpr int largest_power_of_ten(); + static inline constexpr int smallest_power_of_ten(); + static inline constexpr T exact_power_of_ten(std::int64_t power); + static inline constexpr size_t max_digits(); + static inline constexpr equiv_uint exponent_mask(); + static inline constexpr equiv_uint mantissa_mask(); + static inline constexpr equiv_uint hidden_bit_mask(); +}; + +template +struct binary_format_lookup_tables +{ + static constexpr double powers_of_ten[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; + + // Largest integer value v so that (5**index * v) <= 1<<53. + // 0x10000000000000 == 1 << 53 + static constexpr std::uint64_t max_mantissa[] = { + 0x10000000000000, + 0x10000000000000 / 5, + 0x10000000000000 / (5 * 5), + 0x10000000000000 / (5 * 5 * 5), + 0x10000000000000 / (5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555), + 0x10000000000000 / (constant_55555 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)}; +}; + +#if defined(BOOST_NO_CXX17_INLINE_VARIABLES) && BOOST_MSVC != 1900 +template +constexpr double binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; +#endif + +template +struct binary_format_lookup_tables +{ + static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f, 1e7f, 1e8f, 1e9f, 1e10f}; + + // Largest integer value v so that (5**index * v) <= 1<<24. + // 0x1000000 == 1<<24 + static constexpr uint64_t max_mantissa[] = { + 0x1000000, + 0x1000000 / 5, + 0x1000000 / (5 * 5), + 0x1000000 / (5 * 5 * 5), + 0x1000000 / (5 * 5 * 5 * 5), + 0x1000000 / (constant_55555), + 0x1000000 / (constant_55555 * 5), + 0x1000000 / (constant_55555 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * constant_55555), + 0x1000000 / (constant_55555 * constant_55555 * 5)}; +}; + +#if defined(BOOST_NO_CXX17_INLINE_VARIABLES) && BOOST_MSVC != 1900 +template +constexpr float binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; +#endif + +template <> +inline constexpr int binary_format::min_exponent_fast_path() +{ + #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + + return 0; + + #else + + return -22; + + #endif +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() +{ + #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + + return 0; + + #else + + return -10; + + #endif +} + +template <> +inline constexpr int binary_format::mantissa_explicit_bits() +{ + return 52; +} +template <> +inline constexpr int binary_format::mantissa_explicit_bits() +{ + return 23; +} + +template <> +inline constexpr int binary_format::max_exponent_round_to_even() +{ + return 23; +} +template <> +inline constexpr int binary_format::max_exponent_round_to_even() +{ + return 10; +} + +template <> +inline constexpr int binary_format::min_exponent_round_to_even() +{ + return -4; +} +template <> +inline constexpr int binary_format::min_exponent_round_to_even() +{ + return -17; +} + +template <> +inline constexpr int binary_format::minimum_exponent() +{ + return -1023; +} +template <> inline constexpr int binary_format::minimum_exponent() +{ + return -127; +} + +template <> +inline constexpr int binary_format::infinite_power() +{ + return 0x7FF; +} +template <> +inline constexpr int binary_format::infinite_power() +{ + return 0xFF; +} + +template <> +inline constexpr int binary_format::sign_index() +{ + return 63; +} +template <> +inline constexpr int binary_format::sign_index() +{ + return 31; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() +{ + return 22; +} +template <> +inline constexpr int binary_format::max_exponent_fast_path() +{ + return 10; +} + +template <> +inline constexpr std::uint64_t binary_format::max_mantissa_fast_path() +{ + return static_cast(2) << mantissa_explicit_bits(); +} +template <> +inline constexpr std::uint64_t binary_format::max_mantissa_fast_path(std::int64_t power) +{ + // caller is responsible to ensure that + // power >= 0 && power <= 22 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return static_cast(max_mantissa[0]), max_mantissa[power]; +} + +template <> +inline constexpr std::uint64_t binary_format::max_mantissa_fast_path() +{ + return static_cast(2) << mantissa_explicit_bits(); +} +template <> +inline constexpr uint64_t binary_format::max_mantissa_fast_path(std::int64_t power) +{ + // caller is responsible to ensure that + // power >= 0 && power <= 10 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return static_cast(max_mantissa[0]), max_mantissa[power]; +} + +template <> +inline constexpr double binary_format::exact_power_of_ten(std::int64_t power) +{ + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return static_cast(powers_of_ten[0]), powers_of_ten[power]; +} +template <> +inline constexpr float binary_format::exact_power_of_ten(std::int64_t power) +{ + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return static_cast(powers_of_ten[0]), powers_of_ten[power]; +} + +template <> +inline constexpr int binary_format::largest_power_of_ten() +{ + return 308; +} +template <> +inline constexpr int binary_format::largest_power_of_ten() +{ + return 38; +} + +template <> +inline constexpr int binary_format::smallest_power_of_ten() +{ + return -342; +} +template <> +inline constexpr int binary_format::smallest_power_of_ten() +{ + return -65; +} + +template <> +inline constexpr std::size_t binary_format::max_digits() +{ + return 769; +} +template <> inline constexpr std::size_t binary_format::max_digits() +{ + return 114; +} + +template <> +inline constexpr binary_format::equiv_uint binary_format::exponent_mask() +{ + return 0x7F800000; +} +template <> +inline constexpr binary_format::equiv_uint binary_format::exponent_mask() +{ + return 0x7FF0000000000000; +} + +template <> +inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() +{ + return 0x007FFFFF; +} +template <> +inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() +{ + return 0x000FFFFFFFFFFFFF; +} + +template <> +inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() +{ + return 0x00800000; +} +template <> +inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() +{ + return 0x0010000000000000; +} + +template +BOOST_FORCEINLINE BOOST_CHARCONV_CXX20_CONSTEXPR void to_float(bool negative, adjusted_mantissa am, T &value) +{ + using uint = typename binary_format::equiv_uint; + uint word = static_cast(am.mantissa); + word |= static_cast(am.power2) << binary_format::mantissa_explicit_bits(); + word |= static_cast(negative) << binary_format::sign_index(); + + #ifdef BOOST_CHARCONV_HAS_STD_BITCAST + // std::bit_cast is constexpr whereas the boost version is not + value = std::bit_cast(word); + + #else + + value = boost::core::bit_cast(word); + + #endif +} + +template +static constexpr std::uint64_t int_cmp_zeros() +{ + static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size"); + return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? + (static_cast(static_cast('0')) << 48 | + static_cast(static_cast('0')) << 32 | + static_cast(static_cast('0')) << 16 | static_cast('0')) : + (static_cast(static_cast('0')) << 32 | static_cast('0')); +} + +template +static constexpr int int_cmp_len() +{ + return sizeof(std::uint64_t) / sizeof(UC); +} + +template +static constexpr const UC* str_const_nan() +{ + return nullptr; +} + +template<> +constexpr const char* str_const_nan() +{ + return "nan"; +} +template<> +constexpr const wchar_t* str_const_nan() +{ + return L"nan"; +} +template<> +constexpr const char16_t* str_const_nan() +{ + return u"nan"; +} +template<> +constexpr const char32_t* str_const_nan() +{ + return U"nan"; +} +template +static constexpr const UC* str_const_inf() +{ + return nullptr; +} +template<> +constexpr const char* str_const_inf() +{ + return "infinity"; +} +template<> +constexpr const wchar_t* str_const_inf() +{ + return L"infinity"; +} +template<> +constexpr const char16_t* str_const_inf() +{ + return u"infinity"; +} +template<> +constexpr const char32_t* str_const_inf() +{ + return U"infinity"; +} + +}}}} // Namespaces + +#endif // BOOST_CHARCONV_DETAIL_FAST_FLOAT_FLOAT_COMMON_HPP