diff --git a/include/fast_float/digit_comparison.h b/include/fast_float/digit_comparison.h index 303fff91..0c170b98 100644 --- a/include/fast_float/digit_comparison.h +++ b/include/fast_float/digit_comparison.h @@ -72,7 +72,7 @@ to_extended(T value) noexcept { binary_format::minimum_exponent(); equiv_uint bits; #if FASTFLOAT_HAS_BIT_CAST - bits = std::bit_cast(value); + bits = bit_cast(value); #else ::memcpy(&bits, &value, sizeof(T)); #endif diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index ddee4ce6..fb60416c 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -10,6 +10,7 @@ #ifdef __has_include #if __has_include() && (__cplusplus > 202002L || _MSVC_LANG > 202002L) #include + #endif #endif #include "constexpr_feature_detect.h" @@ -202,9 +203,98 @@ fastfloat_really_inline constexpr bool is_supported_float_type() { #if __STDCPP_FLOAT64_T__ || std::is_same::value #endif +#ifdef __STDCPP_FLOAT16_T__ + || std::is_same::value +#endif // __STDCPP_FLOAT16_T__ +#ifdef _STDCPP_BFLOAT16 + || std::is_same::value +#endif // _STDCPP_BFLOAT16 ; } +union float_union { + float f; + uint32_t bits; +}; + +union double_union { + double f; + uint64_t bits; +}; + +template +constexpr T bit_cast(const U &u); + +template<> +fastfloat_really_inline constexpr float bit_cast(const uint32_t &u) { + float_union fu; + fu.bits = u; + return fu.f; +} + +template<> +fastfloat_really_inline constexpr double bit_cast(const uint64_t &u) { + double_union fu; + fu.bits = u; + return fu.f; +} + +template<> +fastfloat_really_inline constexpr uint32_t bit_cast(const float &u) { + float_union fu; + fu.f = u; + return fu.bits; +} + +template<> +fastfloat_really_inline constexpr uint64_t bit_cast(const double &u) { + double_union fu; + fu.f = u; + return fu.bits; +} + +#ifdef __STDCPP_FLOAT16_T__ +union float16_union { + std::float16_t f; + uint16_t bits; +}; + +template<> +fastfloat_really_inline constexpr uint16_t bit_cast(const std::float16_t &u) { + float16_union fu; + fu.f = u; + return fu.bits; +} + +template<> +fastfloat_really_inline constexpr std::float16_t bit_cast(const uint16_t &u) { + float16_union fu; + fu.bits = u; + return fu.f; +} +#endif // __STDCPP_FLOAT16_T__ +#ifdef __STDCPP_BFLOAT16_T__ +union bfloat16_union { + std::bfloat16_t f; + uint16_t bits; +}; + +template<> +fastfloat_really_inline constexpr uint16_t bit_cast(const std::bfloat16_t &u) { + bfloat16_union fu; + fu.f = u; + return fu.bits; +} + +template<> +fastfloat_really_inline constexpr std::bfloat16_t bit_cast(const uint16_t &u) { + bfloat16_union fu; + fu.bits = u; + return fu.f; +} +#endif // __STDCPP_BFLOAT16_T__ + + template fastfloat_really_inline constexpr bool is_supported_char_type() { return std::is_same::value || std::is_same::value || @@ -377,7 +467,7 @@ template struct binary_format_lookup_tables; template struct binary_format : binary_format_lookup_tables { using equiv_uint = - typename std::conditional::type; + typename std::conditional::type>::type; static inline constexpr int mantissa_explicit_bits(); static inline constexpr int minimum_exponent(); @@ -556,6 +646,215 @@ template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } + +// credit: Jakub Jelínek +#ifdef __STDCPP_FLOAT16_T__ +template struct binary_format_lookup_tables { + static constexpr std::float16_t powers_of_ten[] = {1};// todo: fix this + static constexpr uint64_t max_mantissa[] = {1};// todo: fix this +}; + +template +constexpr std::float16_t + binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t + binary_format_lookup_tables::max_mantissa[]; + +template <> +inline constexpr std::float16_t +binary_format::exact_power_of_ten(int64_t power) { + return powers_of_ten[power]; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7C00; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x03FF; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x0400; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 0; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path() { + return 0; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + return max_mantissa[power]; +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { + return 0; +} +template <> +constexpr int binary_format::mantissa_explicit_bits() { + return 10; +} + +template <> +constexpr int binary_format::max_exponent_round_to_even() { + return 5; +} + +template <> +constexpr int binary_format::min_exponent_round_to_even() { + return -22; +} + +template <> constexpr int binary_format::minimum_exponent() { + return -15; +} + +template <> constexpr int binary_format::infinite_power() { + return 0x1F; +} + +template <> constexpr int binary_format::sign_index() { + return 15; +} + +template <> +constexpr int binary_format::largest_power_of_ten() { + return 4; +} + +template <> +constexpr int binary_format::smallest_power_of_ten() { + return -27; +} + +template <> constexpr size_t binary_format::max_digits() { + return 22; +} + +#endif + +// credit: Jakub Jelínek +#ifdef __STDCPP_BFLOAT16_T__ +template struct binary_format_lookup_tables { + static constexpr std::bfloat16_t powers_of_ten[] = {1};// todo: fix this + + static constexpr uint64_t max_mantissa[] = {1};// todo: fix this +}; + +template +constexpr std::bfloat16_t + binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t + binary_format_lookup_tables::max_mantissa[]; + +template <> +inline constexpr std::bfloat16_t +binary_format::exact_power_of_ten(int64_t power) { + return (void)powers_of_ten[0], powers_of_ten[power]; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 0; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7F80; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x007F; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x0080; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path() { + return 0; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + return max_mantissa[power]; +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { + return 0; +} + +template <> +constexpr int binary_format::mantissa_explicit_bits() { + return 7; +} + +template <> +constexpr int binary_format::max_exponent_round_to_even() { + return 3; +} + +template <> +constexpr int binary_format::min_exponent_round_to_even() { + return -24; +} + +template <> constexpr int binary_format::minimum_exponent() { + return -127; +} + +template <> constexpr int binary_format::infinite_power() { + return 0xFF; +} + +template <> constexpr int binary_format::sign_index() { + return 15; +} + +template <> +constexpr int binary_format::largest_power_of_ten() { + return 38; +} + +template <> +constexpr int binary_format::smallest_power_of_ten() { + return -60; +} + +template <> constexpr size_t binary_format::max_digits() { + return 98; +} + +#endif + template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { @@ -660,6 +959,32 @@ to_float(bool negative, adjusted_mantissa am, T &value) { ::memcpy(&value, &word, sizeof(T)); #endif } +#ifdef __STDCPP_FLOAT16_T__ +template<> +fastfloat_really_inline void +to_float(bool negative, adjusted_mantissa am, + std::float16_t &value) +{ + constexpr int mantissa_bits + = binary_format::mantissa_explicit_bits(); + value = bit_cast (uint16_t(am.mantissa + | (uint16_t(am.power2) << mantissa_bits) + | (negative ? 0x8000 : 0))); +} +#endif // __STDCPP_FLOAT16_T__ +#ifdef __STDCPP_BFLOAT16_T__ +template<> +fastfloat_really_inline void +to_float(bool negative, adjusted_mantissa am, + std::bfloat16_t &value) +{ + constexpr int mantissa_bits + = binary_format::mantissa_explicit_bits(); + value = bit_cast(uint16_t(am.mantissa + | (uint16_t(am.power2) << mantissa_bits) + | (negative ? 0x8000 : 0))); +} +#endif // __STDCPP_BFLOAT16_T__ #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default template struct space_lut { @@ -780,6 +1105,21 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; } + + +static_assert(std::is_same::equiv_uint, uint64_t>::value, + "equiv_uint should be uint64_t for double"); +static_assert(std::is_same::equiv_uint, uint32_t>::value, + "equiv_uint should be uint32_t for float"); +#ifdef __STDCPP_FLOAT16_T__ +static_assert(std::is_same::equiv_uint, uint16_t>::value, + "equiv_uint should be uint16_t for std::float16_t"); +#endif // __STDCPP_FLOAT16_T__ +#ifdef __STDCPP_BFLOAT16_T__ +static_assert(std::is_same::equiv_uint, uint16_t>::value, + "equiv_uint should be uint16_t for std::bfloat16_t"); +#endif // __STDCPP_BFLOAT16_T__ + } // namespace fast_float #endif diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 6d883fb9..4d00d230 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -182,7 +182,6 @@ template <> struct from_chars_caller { } }; #endif - template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, @@ -199,12 +198,10 @@ from_chars(UC const *first, UC const *last, T &value, template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { - static_assert(is_supported_float_type(), "only some floating-point types are supported"); static_assert(is_supported_char_type(), "only char, wchar_t, char16_t and char32_t are supported"); - from_chars_result_t answer; answer.ec = std::errc(); // be optimistic diff --git a/tests/basictest.cpp b/tests/basictest.cpp index a66ccaf6..a1afa613 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -275,6 +275,16 @@ bool check_file(std::string file_name) { std::string str; while (std::getline(newfile, str)) { if (str.size() > 0) { +#ifdef __STDCPP_FLOAT16_T__ + // Read 16-bit hex + uint16_t float16; + auto r16 = std::from_chars(str.data(), str.data() + str.size(), + float16, 16); + if (r16.ec != std::errc()) { + std::cerr << "16-bit parsing failure\n"; + return false; + } +#endif // Read 32-bit hex uint32_t float32; auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(), @@ -294,6 +304,17 @@ bool check_file(std::string file_name) { // The string to parse: const char *number_string = str.data() + 31; const char *end_of_string = str.data() + str.size(); +#ifdef __STDCPP_FLOAT16_T__ + // Parse as 16-bit float + std::float16_t parsed_16{}; + auto fast_float_r16 = + fast_float::from_chars(number_string, end_of_string, parsed_16); + if (fast_float_r16.ec != std::errc() && + fast_float_r16.ec != std::errc::result_out_of_range) { + std::cerr << "16-bit fast_float parsing failure for: " + str + "\n"; + return false; + } +#endif // Parse as 32-bit float float parsed_32; auto fast_float_r32 = @@ -313,11 +334,29 @@ bool check_file(std::string file_name) { return false; } // Convert the floats to unsigned ints. +#ifdef __STDCPP_FLOAT16_T__ + uint16_t float16_parsed; +#endif uint32_t float32_parsed; uint64_t float64_parsed; +#ifdef __STDCPP_FLOAT16_T__ + ::memcpy(&float16_parsed, &parsed_16, sizeof(parsed_16)); + +#endif ::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32)); ::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64)); // Compare with expected results +#ifdef __STDCPP_FLOAT16_T__ + if (float16_parsed != float16) { + std::cout << "bad 16 " << str << std::endl; + std::cout << "parsed as " << iHexAndDec(parsed_16) << std::endl; + std::cout << "as raw uint16_t, parsed = " << float16_parsed + << ", expected = " << float16 << std::endl; + std::cout << "fesetround: " << round_name(d) << std::endl; + fesetround(FE_TONEAREST); + return false; + } +#endif if (float32_parsed != float32) { std::cout << "bad 32 " << str << std::endl; std::cout << "parsed as " << iHexAndDec(parsed_32) << std::endl; diff --git a/tests/example_test.cpp b/tests/example_test.cpp index 32f0559e..03457f3a 100644 --- a/tests/example_test.cpp +++ b/tests/example_test.cpp @@ -78,29 +78,41 @@ constexpr double constexptest() { return parse("3.1415 input"); } #endif bool small() { - double result = -1; - std::string str = "3e-1000"; - auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); - if(r.ec != std::errc::result_out_of_range) { return false; } - if(r.ptr != str.data() + 7) { return false; } - if(result != 0) { return false; } + double result = -1; + std::string str = "3e-1000"; + auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); + if (r.ec != std::errc::result_out_of_range) { + return false; + } + if (r.ptr != str.data() + 7) { + return false; + } + if (result != 0) { + return false; + } printf("small values go to zero\n"); - return true; + return true; } bool large() { - double result = -1; - std::string str = "3e1000"; - auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); - if(r.ec != std::errc::result_out_of_range) { return false; } - if(r.ptr != str.data() + 6) { return false; } - if(result != std::numeric_limits::infinity()) { return false; } + double result = -1; + std::string str = "3e1000"; + auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); + if (r.ec != std::errc::result_out_of_range) { + return false; + } + if (r.ptr != str.data() + 6) { + return false; + } + if (result != std::numeric_limits::infinity()) { + return false; + } printf("large values go to infinity\n"); - return true; + return true; } int main() { - const std::string input = "3.1416 xyz "; + std::string input = "3.1416 xyz "; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); @@ -109,6 +121,20 @@ int main() { return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; +#ifdef __STDCPP_FLOAT16_T__ + printf("16-bit float\n"); + // Parse as 16-bit float + std::float16_t parsed_16{}; + input = "10000e-1452"; + auto fast_float_r16 = + fast_float::from_chars(input.data(), input.data() + input.size(), parsed_16); + if (fast_float_r16.ec != std::errc() && + fast_float_r16.ec != std::errc::result_out_of_range) { + std::cerr << "16-bit fast_float parsing failure for: " + input + "\n"; + return false; + } + std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl; +#endif if (!small()) { printf("Bug\n"); return EXIT_FAILURE;