From bfcff49c83b9027768374103f6d7efbdca08868c Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 23 Aug 2024 21:11:07 -0400 Subject: [PATCH 01/20] 16-bit float support --- include/fast_float/float_common.h | 167 +++++++++++++++++++++++++++++- tests/basictest.cpp | 39 +++++++ tests/example_test.cpp | 15 ++- 3 files changed, 215 insertions(+), 6 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 38f7759..0334c64 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -221,15 +221,21 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { template struct is_supported_float_type - : std::integral_constant::value || - std::is_same::value + : std::integral_constant< + bool, std::is_same::value || std::is_same::value #ifdef __STDCPP_FLOAT32_T__ - || std::is_same::value + || std::is_same::value #endif #ifdef __STDCPP_FLOAT64_T__ - || std::is_same::value + || std::is_same::value #endif - > { +#ifdef __STDCPP_FLOAT16_T__ + || std::is_same::value +#endif +#ifdef __STDCPP_BFLOAT16_T__ + || std::is_same::value +#endif + > { }; template @@ -622,6 +628,157 @@ 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[] = {}; + static constexpr uint64_t max_mantissa[] = {}; +}; + +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 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 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[] = {}; + + static constexpr uint64_t max_mantissa[] = {}; +}; + +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 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 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) { diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 0b0950c..ec9affd 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: char const *number_string = str.data() + 31; char const *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 456572f..447eedf 100644 --- a/tests/example_test.cpp +++ b/tests/example_test.cpp @@ -112,7 +112,7 @@ bool large() { } int main() { - std::string const 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); @@ -121,6 +121,19 @@ int main() { return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; +#ifdef __STDCPP_FLOAT16_T__ + // 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; From c5268999516bf6519b823553a4f911f3b257eeb2 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 23 Aug 2024 21:12:27 -0400 Subject: [PATCH 02/20] cleaning. --- tests/basictest.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index ec9affd..04f5bcb 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -307,13 +307,13 @@ bool check_file(std::string file_name) { #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; - // } + 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; From 6f8fd6728dad06a8987b1f8daf843daab9071af9 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 13 Sep 2024 16:44:52 -0400 Subject: [PATCH 03/20] make it build --- include/fast_float/digit_comparison.h | 2 +- include/fast_float/float_common.h | 210 +++++++++++++++++++++++++- tests/example_test.cpp | 1 + 3 files changed, 205 insertions(+), 8 deletions(-) diff --git a/include/fast_float/digit_comparison.h b/include/fast_float/digit_comparison.h index d7ef3d9..c870b14 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 0334c64..8267347 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -260,6 +260,88 @@ struct is_supported_char_type > { }; +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__ + // Compares two ASCII strings in a case insensitive manner. template inline FASTFLOAT_CONSTEXPR14 bool @@ -630,12 +712,13 @@ inline constexpr uint64_t binary_format::max_mantissa_fast_path() { // credit: Jakub Jelínek #ifdef __STDCPP_FLOAT16_T__ - template struct binary_format_lookup_tables { - static constexpr std::float16_t powers_of_ten[] = {}; - static constexpr uint64_t max_mantissa[] = {}; + static constexpr std::float16_t powers_of_ten[] = {1}; // todo: fix this + static constexpr uint64_t max_mantissa[] = {1}; // todo: fix this }; +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + template constexpr std::float16_t binary_format_lookup_tables::powers_of_ten[]; @@ -644,6 +727,33 @@ template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; +#endif + +template <> +inline constexpr std::float16_t +binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)powers_of_ten[0], 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; @@ -655,6 +765,13 @@ binary_format::max_mantissa_fast_path() { return 0; } +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + template <> inline constexpr int binary_format::min_exponent_fast_path() { return 0; @@ -705,13 +822,13 @@ template <> constexpr size_t binary_format::max_digits() { // credit: Jakub Jelínek #ifdef __STDCPP_BFLOAT16_T__ - template struct binary_format_lookup_tables { - static constexpr std::bfloat16_t powers_of_ten[] = {}; - - static constexpr uint64_t max_mantissa[] = {}; + static constexpr std::bfloat16_t powers_of_ten[] = {1}; // todo: fix this + static constexpr uint64_t max_mantissa[] = {1}; // todo: fix this }; +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + template constexpr std::bfloat16_t binary_format_lookup_tables::powers_of_ten[]; @@ -720,17 +837,51 @@ template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; +#endif + +template <> +inline constexpr std::bfloat16_t +binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + 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) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + template <> inline constexpr int binary_format::min_exponent_fast_path() { return 0; @@ -892,6 +1043,34 @@ to_float(bool negative, adjusted_mantissa am, T &value) { #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__ + template struct space_lut { static constexpr bool value[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -944,10 +1123,12 @@ template <> constexpr char16_t const *str_const_nan() { template <> constexpr char32_t const *str_const_nan() { return U"nan"; } + #ifdef __cpp_char8_t template <> constexpr char8_t const *str_const_nan() { return u8"nan"; } + #endif template constexpr UC const *str_const_inf(); @@ -965,10 +1146,12 @@ template <> constexpr char16_t const *str_const_inf() { template <> constexpr char32_t const *str_const_inf() { return U"infinity"; } + #ifdef __cpp_char8_t template <> constexpr char8_t const *str_const_inf() { return u8"infinity"; } + #endif template struct int_luts { @@ -1038,6 +1221,7 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { static_assert(std::is_same, uint64_t>::value, "equiv_uint should be uint64_t for double"); + static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for float"); @@ -1051,6 +1235,18 @@ static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for std::float32_t"); #endif +#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 + +#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 + constexpr chars_format operator~(chars_format rhs) noexcept { using int_type = std::underlying_type::type; return static_cast(~static_cast(rhs)); diff --git a/tests/example_test.cpp b/tests/example_test.cpp index 447eedf..cefaf29 100644 --- a/tests/example_test.cpp +++ b/tests/example_test.cpp @@ -122,6 +122,7 @@ int main() { } 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"; From 8cee0250828d682d0405221c02b83b53dc564919 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Sun, 1 Dec 2024 20:03:47 +0100 Subject: [PATCH 04/20] add powers_of_ten/max_mantissa for float16_t/bfloat16_t --- include/fast_float/float_common.h | 37 ++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 8267347..9b59735 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -713,8 +713,17 @@ inline constexpr uint64_t binary_format::max_mantissa_fast_path() { // 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 + static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16, + 1e3f16, 1e4f16}; + + // Largest integer value v so that (5**index * v) <= 1<<11. + // 0x800 == 1<<11 + static constexpr uint64_t max_mantissa[] = {0x800, + 0x800 / 5, + 0x800 / (5 * 5), + 0x800 / (5 * 5 * 5), + 0x800 / (5 * 5 * 5 * 5), + 0x800 / (constant_55555)}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE @@ -756,18 +765,21 @@ binary_format::hidden_bit_mask() { template <> inline constexpr int binary_format::max_exponent_fast_path() { - return 0; + return 4; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return 0; + return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 4 + // // Work around clang bug https://godbolt.org/z/zedh7rrhc return (void)max_mantissa[0], max_mantissa[power]; } @@ -823,8 +835,14 @@ template <> constexpr size_t binary_format::max_digits() { // 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 + static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16, + 1e3bf16}; + + // Largest integer value v so that (5**index * v) <= 1<<8. + // 0x100 == 1<<8 + static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5), + 0x100 / (5 * 5 * 5), + 0x100 / (5 * 5 * 5 * 5)}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE @@ -848,7 +866,7 @@ binary_format::exact_power_of_ten(int64_t power) { template <> inline constexpr int binary_format::max_exponent_fast_path() { - return 0; + return 3; } template <> @@ -872,12 +890,15 @@ binary_format::hidden_bit_mask() { template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return 0; + return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 3 + // // Work around clang bug https://godbolt.org/z/zedh7rrhc return (void)max_mantissa[0], max_mantissa[power]; } From 558bec8b9b72e7bc931d6a92f5b63ce047d9fcc1 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Mon, 2 Dec 2024 11:44:30 +0100 Subject: [PATCH 05/20] fix logging in basictest --- tests/basictest.cpp | 91 +++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 04f5bcb..13fb663 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -3,15 +3,17 @@ #include "doctest/doctest.h" #include "fast_float/fast_float.h" +#include #include #include #include #include +#include #include +#include #include #include #include -#include #if FASTFLOAT_IS_CONSTEXPR #ifndef FASTFLOAT_CONSTEXPR_TESTS @@ -54,9 +56,14 @@ #define FASTFLOAT_ODDPLATFORM 1 #endif -#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")" +#define iHexAndDec(v) \ + (std::ostringstream{} << std::hex << "0x" << (v) << " (" << std::dec << (v) \ + << ")") \ + .str() #define fHexAndDec(v) \ - std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")" + (std::ostringstream{} << std::hexfloat << (v) << " (" << std::defaultfloat \ + << (v) << ")") \ + .str() char const *round_name(int d) { switch (d) { @@ -256,7 +263,6 @@ TEST_CASE("parse_negative_zero") { #if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && \ !defined(FASTFLOAT_ODDPLATFORM) -#include #include #include @@ -722,6 +728,38 @@ uint64_t get_mantissa(double f) { return (m & ((uint64_t(1) << 57) - 1)); } +#ifdef __STDCPP_FLOAT64_T__ +uint64_t get_mantissa(std::float64_t f) { + uint64_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint64_t(1) << 10) - 1)); +} +#endif + +#ifdef __STDCPP_FLOAT32_T__ +uint32_t get_mantissa(std::float32_t f) { + uint32_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint32_t(1) << 10) - 1)); +} +#endif + +#ifdef __STDCPP_FLOAT16_T__ +uint16_t get_mantissa(std::float16_t f) { + uint16_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint16_t(1) << 10) - 1)); +} +#endif + +#ifdef __STDCPP_BFLOAT16_T__ +uint16_t get_mantissa(std::bfloat16_t f) { + uint16_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint16_t(1) << 7) - 1)); +} +#endif + std::string append_zeros(std::string str, size_t number_of_zeros) { std::string answer(str); for (size_t i = 0; i < number_of_zeros; i++) { @@ -736,19 +774,12 @@ enum class Diag { runtime, comptime }; } // anonymous namespace +constexpr size_t global_string_capacity = 2048; + template constexpr void check_basic_test_result(stringtype str, result_type result, T actual, T expected, std::errc expected_ec) { - if constexpr (diag == Diag::runtime) { - INFO("str=" << str << "\n" - << " expected=" << fHexAndDec(expected) << "\n" - << " ..actual=" << fHexAndDec(actual) << "\n" - << " expected mantissa=" << iHexAndDec(get_mantissa(expected)) - << "\n" - << " ..actual mantissa=" << iHexAndDec(get_mantissa(actual))); - } - struct ComptimeDiag { // Purposely not constexpr static void error_not_equal() {} @@ -756,6 +787,18 @@ constexpr void check_basic_test_result(stringtype str, result_type result, #define FASTFLOAT_CHECK_EQ(...) \ if constexpr (diag == Diag::runtime) { \ + char narrow[global_string_capacity]{}; \ + for (size_t i = 0; i < str.size(); i++) { \ + narrow[i] = char(str[i]); \ + } \ + INFO("str(char" << 8 * sizeof(typename stringtype::value_type) \ + << ")=" << narrow << "\n" \ + << " expected=" << fHexAndDec(expected) << "\n" \ + << " ..actual=" << fHexAndDec(actual) << "\n" \ + << " expected mantissa=" \ + << iHexAndDec(get_mantissa(expected)) << "\n" \ + << " ..actual mantissa=" \ + << iHexAndDec(get_mantissa(actual))); \ CHECK_EQ(__VA_ARGS__); \ } else { \ if ([](auto const &lhs, auto const &rhs) { \ @@ -774,9 +817,9 @@ constexpr void check_basic_test_result(stringtype str, result_type result, return -x; } return x; - } + } else #endif - return std::copysign(x, y); + return std::copysign(x, y); }; auto isnan = [](double x) -> bool { return x != x; }; @@ -797,26 +840,26 @@ constexpr void basic_test(std::string_view str, T expected, auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); check_basic_test_result(str, result, actual, expected, expected_ec); - constexpr size_t global_string_capacity = 2048; if (str.size() > global_string_capacity) { return; } + // We give plenty of memory: 2048 characters. char16_t u16[global_string_capacity]{}; - for (size_t i = 0; i < str.size(); i++) { u16[i] = char16_t(str[i]); } + auto result16 = fast_float::from_chars(u16, u16 + str.size(), actual); check_basic_test_result(std::u16string_view(u16, str.size()), result16, actual, expected, expected_ec); char32_t u32[global_string_capacity]{}; - for (size_t i = 0; i < str.size(); i++) { u32[i] = char32_t(str[i]); } + auto result32 = fast_float::from_chars(u32, u32 + str.size(), actual); check_basic_test_result(std::u32string_view(u32, str.size()), result32, actual, expected, expected_ec); @@ -906,7 +949,7 @@ void basic_test(float val) { basic_test(val); \ } -TEST_CASE("64bit.inf") { +TEST_CASE("double.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); @@ -934,7 +977,7 @@ TEST_CASE("64bit.inf") { std::errc::result_out_of_range); } -TEST_CASE("64bit.general") { +TEST_CASE("double.general") { verify("0.95000000000000000000", 0.95); verify("22250738585072012e-324", 0x1p-1022); /* limit between normal and subnormal*/ @@ -1118,7 +1161,7 @@ TEST_CASE("64bit.general") { -0x1.f1660a65b00bfp+60); } -TEST_CASE("64bit.decimal_point") { +TEST_CASE("double.decimal_point") { constexpr auto options = [] { fast_float::parse_options ret{}; ret.decimal_point = ','; @@ -1274,7 +1317,7 @@ TEST_CASE("64bit.decimal_point") { 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); } -TEST_CASE("32bit.inf") { +TEST_CASE("float.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); @@ -1292,7 +1335,7 @@ TEST_CASE("32bit.inf") { std::errc::result_out_of_range); } -TEST_CASE("32bit.general") { +TEST_CASE("float.general") { verify("-1e-999", -0.0f, std::errc::result_out_of_range); verify("1." "175494140627517859246175898662808184331245864732796240031385942718174" @@ -1432,7 +1475,7 @@ TEST_CASE("32bit.general") { 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); } -TEST_CASE("32bit.decimal_point") { +TEST_CASE("float.decimal_point") { constexpr auto options = [] { fast_float::parse_options ret{}; ret.decimal_point = ','; From 74e00e140176fdec80febce5b212234b3ea7e68a Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Mon, 2 Dec 2024 11:45:05 +0100 Subject: [PATCH 06/20] fix double test in float region in basictest --- tests/basictest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 13fb663..f34ab02 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1561,7 +1561,7 @@ TEST_CASE("float.decimal_point") { "0," "000000000000000000000000000000000000011754943508222875079687365372222456" "778186655567720875215087517062784172594547271728515625", - 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625); + 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625f); verify_options( "0," "000000000000000000000000000000000000000000001401298464324817070923729583" From b3acae22eae97a14180a59e7bdfa7f425c187c54 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Mon, 2 Dec 2024 16:25:12 +0100 Subject: [PATCH 07/20] fix parse_zero and parse_negative_zero output --- tests/basictest.cpp | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index f34ab02..7a45258 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -159,7 +159,7 @@ TEST_CASE("rounds_to_nearest") { #endif } -TEST_CASE("parse_zero") { +TEST_CASE("double.parse_zero") { // // If this function fails, we may be left in a non-standard rounding state. // @@ -172,41 +172,45 @@ TEST_CASE("parse_zero") { fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(zero, zero + 1, f); CHECK(r1.ec == std::errc()); - std::cout << "FE_UPWARD parsed zero as " << iHexAndDec(f) << std::endl; + std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(zero, zero + 1, f); CHECK(r2.ec == std::errc()); - std::cout << "FE_TOWARDZERO parsed zero as " << iHexAndDec(f) << std::endl; + std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(zero, zero + 1, f); CHECK(r3.ec == std::errc()); - std::cout << "FE_DOWNWARD parsed zero as " << iHexAndDec(f) << std::endl; + std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(zero, zero + 1, f); CHECK(r4.ec == std::errc()); - std::cout << "FE_TONEAREST parsed zero as " << iHexAndDec(f) << std::endl; + std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0); } -TEST_CASE("parse_negative_zero") { +TEST_CASE("double.parse_negative_zero") { // // If this function fails, we may be left in a non-standard rounding state. // @@ -219,41 +223,45 @@ TEST_CASE("parse_negative_zero") { fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r1.ec == std::errc()); - std::cout << "FE_UPWARD parsed negative zero as " << iHexAndDec(f) + std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r2.ec == std::errc()); - std::cout << "FE_TOWARDZERO parsed negative zero as " << iHexAndDec(f) + std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r3.ec == std::errc()); - std::cout << "FE_DOWNWARD parsed negative zero as " << iHexAndDec(f) + std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r4.ec == std::errc()); - std::cout << "FE_TONEAREST parsed negative zero as " << iHexAndDec(f) + std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0); ::memcpy(&float64_parsed, &f, sizeof(f)); - std::cout << "double as uint64_t is " << float64_parsed << std::endl; + std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) + << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); } From c62b8536488b05b9f24631820ea1a4f49990b462 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Mon, 2 Dec 2024 16:30:37 +0100 Subject: [PATCH 08/20] float.rounds_to_nearest --- tests/basictest.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 7a45258..7ae745e 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -127,32 +127,32 @@ TEST_CASE("system_info") { std::cout << std::endl; } -TEST_CASE("rounds_to_nearest") { +TEST_CASE("float.rounds_to_nearest") { // // If this function fails, we may be left in a non-standard rounding state. // static float volatile fmin = std::numeric_limits::min(); fesetround(FE_UPWARD); - std::cout << "FE_UPWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_UPWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_UPWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_DOWNWARD); - std::cout << "FE_DOWNWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_DOWNWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_DOWNWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TOWARDZERO); - std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_TOWARDZERO); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TONEAREST); - std::cout << "FE_TONEAREST: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_TONEAREST: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_TONEAREST); #if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0) CHECK(fast_float::detail::rounds_to_nearest() == true); From 3b9ff76143c48e7f8aaebd7ddb3e285699bf06de Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Mon, 2 Dec 2024 16:39:33 +0100 Subject: [PATCH 09/20] duplicate tests for both float and double --- tests/basictest.cpp | 158 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 7ae745e..73aa747 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -127,32 +127,32 @@ TEST_CASE("system_info") { std::cout << std::endl; } -TEST_CASE("float.rounds_to_nearest") { +TEST_CASE("double.rounds_to_nearest") { // // If this function fails, we may be left in a non-standard rounding state. // - static float volatile fmin = std::numeric_limits::min(); + static double volatile fmin = std::numeric_limits::min(); fesetround(FE_UPWARD); - std::cout << "FE_UPWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_UPWARD: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) + << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_UPWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_DOWNWARD); - std::cout << "FE_DOWNWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_DOWNWARD: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) + << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_DOWNWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TOWARDZERO); - std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_TOWARDZERO: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) + << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_TOWARDZERO); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TONEAREST); - std::cout << "FE_TONEAREST: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) - << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + std::cout << "FE_TONEAREST: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) + << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_TONEAREST); #if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0) CHECK(fast_float::detail::rounds_to_nearest() == true); @@ -265,6 +265,144 @@ TEST_CASE("double.parse_negative_zero") { CHECK(float64_parsed == 0x8000'0000'0000'0000); } +TEST_CASE("float.rounds_to_nearest") { + // + // If this function fails, we may be left in a non-standard rounding state. + // + static float volatile fmin = std::numeric_limits::min(); + fesetround(FE_UPWARD); + std::cout << "FE_UPWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + CHECK(fegetround() == FE_UPWARD); + CHECK(fast_float::detail::rounds_to_nearest() == false); + + fesetround(FE_DOWNWARD); + std::cout << "FE_DOWNWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + CHECK(fegetround() == FE_DOWNWARD); + CHECK(fast_float::detail::rounds_to_nearest() == false); + + fesetround(FE_TOWARDZERO); + std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + CHECK(fegetround() == FE_TOWARDZERO); + CHECK(fast_float::detail::rounds_to_nearest() == false); + + fesetround(FE_TONEAREST); + std::cout << "FE_TONEAREST: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) + << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; + CHECK(fegetround() == FE_TONEAREST); +#if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0) + CHECK(fast_float::detail::rounds_to_nearest() == true); +#endif +} + +TEST_CASE("float.parse_zero") { + // + // If this function fails, we may be left in a non-standard rounding state. + // + char const *zero = "0"; + uint32_t float32_parsed; + float f = 0; + ::memcpy(&float32_parsed, &f, sizeof(f)); + CHECK(float32_parsed == 0); + + fesetround(FE_UPWARD); + auto r1 = fast_float::from_chars(zero, zero + 1, f); + CHECK(r1.ec == std::errc()); + std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0); + + fesetround(FE_TOWARDZERO); + auto r2 = fast_float::from_chars(zero, zero + 1, f); + CHECK(r2.ec == std::errc()); + std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0); + + fesetround(FE_DOWNWARD); + auto r3 = fast_float::from_chars(zero, zero + 1, f); + CHECK(r3.ec == std::errc()); + std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0); + + fesetround(FE_TONEAREST); + auto r4 = fast_float::from_chars(zero, zero + 1, f); + CHECK(r4.ec == std::errc()); + std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0); +} + +TEST_CASE("float.parse_negative_zero") { + // + // If this function fails, we may be left in a non-standard rounding state. + // + char const *negative_zero = "-0"; + uint32_t float32_parsed; + float f = -0.; + ::memcpy(&float32_parsed, &f, sizeof(f)); + CHECK(float32_parsed == 0x8000'0000); + + fesetround(FE_UPWARD); + auto r1 = fast_float::from_chars(negative_zero, negative_zero + 2, f); + CHECK(r1.ec == std::errc()); + std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) + << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0x8000'0000); + + fesetround(FE_TOWARDZERO); + auto r2 = fast_float::from_chars(negative_zero, negative_zero + 2, f); + CHECK(r2.ec == std::errc()); + std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) + << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0x8000'0000); + + fesetround(FE_DOWNWARD); + auto r3 = fast_float::from_chars(negative_zero, negative_zero + 2, f); + CHECK(r3.ec == std::errc()); + std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) + << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0x8000'0000); + + fesetround(FE_TONEAREST); + auto r4 = fast_float::from_chars(negative_zero, negative_zero + 2, f); + CHECK(r4.ec == std::errc()); + std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) + << std::endl; + CHECK(f == 0); + ::memcpy(&float32_parsed, &f, sizeof(f)); + std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) + << std::endl; + CHECK(float32_parsed == 0x8000'0000); +} + #if FASTFLOAT_SUPPLEMENTAL_TESTS // C++ 17 because it is otherwise annoying to browse all files in a directory. // We also only run these tests on little endian systems. From da819feb74b5bbf10dfd458a611f4e68cabddd56 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Tue, 3 Dec 2024 23:20:45 +0100 Subject: [PATCH 10/20] back to std::bit_cast --- include/fast_float/digit_comparison.h | 2 +- include/fast_float/float_common.h | 110 +++++++++++++++----------- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/include/fast_float/digit_comparison.h b/include/fast_float/digit_comparison.h index c870b14..d7ef3d9 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 = bit_cast(value); + bits = std::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 9b59735..f819fee 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -260,6 +260,7 @@ struct is_supported_char_type > { }; +#if 0 union float_union { float f; uint32_t bits; @@ -341,6 +342,7 @@ fastfloat_really_inline constexpr std::bfloat16_t bit_cast(const uint16_t &u) { return fu.f; } #endif // __STDCPP_BFLOAT16_T__ +#endif // 0 // Compares two ASCII strings in a case insensitive manner. template @@ -519,25 +521,25 @@ template struct binary_format_lookup_tables; template struct binary_format : binary_format_lookup_tables { using equiv_uint = equiv_uint_t; - 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 + static constexpr int mantissa_explicit_bits(); + static constexpr int minimum_exponent(); + static constexpr int infinite_power(); + static constexpr int sign_index(); + static 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 uint64_t max_mantissa_fast_path(int64_t power); - static inline constexpr uint64_t + static constexpr int max_exponent_fast_path(); + static constexpr int max_exponent_round_to_even(); + static constexpr int min_exponent_round_to_even(); + static constexpr uint64_t max_mantissa_fast_path(int64_t power); + static constexpr 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(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(); + static constexpr int largest_power_of_ten(); + static constexpr int smallest_power_of_ten(); + static constexpr T exact_power_of_ten(int64_t power); + static constexpr size_t max_digits(); + static constexpr equiv_uint exponent_mask(); + static constexpr equiv_uint mantissa_mask(); + static constexpr equiv_uint hidden_bit_mask(); }; template struct binary_format_lookup_tables { @@ -710,6 +712,11 @@ inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } +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 { @@ -768,6 +775,11 @@ inline constexpr int binary_format::max_exponent_fast_path() { return 4; } +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 10; +} + template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { @@ -790,43 +802,43 @@ inline constexpr int binary_format::min_exponent_fast_path() { } template <> -constexpr int binary_format::mantissa_explicit_bits() { - return 10; -} - -template <> -constexpr int binary_format::max_exponent_round_to_even() { +inline constexpr int +binary_format::max_exponent_round_to_even() { return 5; } template <> -constexpr int binary_format::min_exponent_round_to_even() { +inline constexpr int +binary_format::min_exponent_round_to_even() { return -22; } -template <> constexpr int binary_format::minimum_exponent() { +template <> +inline constexpr int binary_format::minimum_exponent() { return -15; } -template <> constexpr int binary_format::infinite_power() { +template <> +inline constexpr int binary_format::infinite_power() { return 0x1F; } -template <> constexpr int binary_format::sign_index() { +template <> inline constexpr int binary_format::sign_index() { return 15; } template <> -constexpr int binary_format::largest_power_of_ten() { +inline constexpr int binary_format::largest_power_of_ten() { return 4; } template <> -constexpr int binary_format::smallest_power_of_ten() { +inline constexpr int binary_format::smallest_power_of_ten() { return -27; } -template <> constexpr size_t binary_format::max_digits() { +template <> +inline constexpr size_t binary_format::max_digits() { return 22; } @@ -887,6 +899,11 @@ binary_format::hidden_bit_mask() { return 0x0080; } +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 7; +} + template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { @@ -909,43 +926,43 @@ inline constexpr int binary_format::min_exponent_fast_path() { } template <> -constexpr int binary_format::mantissa_explicit_bits() { - return 7; -} - -template <> -constexpr int binary_format::max_exponent_round_to_even() { +inline constexpr int +binary_format::max_exponent_round_to_even() { return 3; } template <> -constexpr int binary_format::min_exponent_round_to_even() { +inline constexpr int +binary_format::min_exponent_round_to_even() { return -24; } -template <> constexpr int binary_format::minimum_exponent() { +template <> +inline constexpr int binary_format::minimum_exponent() { return -127; } -template <> constexpr int binary_format::infinite_power() { +template <> +inline constexpr int binary_format::infinite_power() { return 0xFF; } -template <> constexpr int binary_format::sign_index() { +template <> inline constexpr int binary_format::sign_index() { return 15; } template <> -constexpr int binary_format::largest_power_of_ten() { +inline constexpr int binary_format::largest_power_of_ten() { return 38; } template <> -constexpr int binary_format::smallest_power_of_ten() { +inline constexpr int binary_format::smallest_power_of_ten() { return -60; } -template <> constexpr size_t binary_format::max_digits() { +template <> +inline constexpr size_t binary_format::max_digits() { return 98; } @@ -961,11 +978,6 @@ binary_format::max_mantissa_fast_path(int64_t power) { return (void)max_mantissa[0], max_mantissa[power]; } -template <> -inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return uint64_t(2) << mantissa_explicit_bits(); -} - template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { @@ -1064,6 +1076,7 @@ to_float(bool negative, adjusted_mantissa am, T &value) { #endif } +#if 0 #ifdef __STDCPP_FLOAT16_T__ template <> fastfloat_really_inline void to_float(bool negative, @@ -1091,6 +1104,7 @@ fastfloat_really_inline void to_float(bool negative, } #endif // __STDCPP_BFLOAT16_T__ +#endif // 0 template struct space_lut { static constexpr bool value[] = { From ac453a091a66a11035e2f192a449d7313ef0ec87 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Tue, 3 Dec 2024 23:22:36 +0100 Subject: [PATCH 11/20] overly precise tests for imprecise floats --- tests/basictest.cpp | 333 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 332 insertions(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 73aa747..2513be5 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -62,7 +62,8 @@ .str() #define fHexAndDec(v) \ (std::ostringstream{} << std::hexfloat << (v) << " (" << std::defaultfloat \ - << (v) << ")") \ + << std::setprecision(DBL_MAX_10_EXP + 1) << (v) \ + << ")") \ .str() char const *round_name(int d) { @@ -1482,6 +1483,11 @@ TEST_CASE("float.inf") { } TEST_CASE("float.general") { + // max + verify("340282346638528859811704183484516925440", 0x1.fffffep+127f); + // -max + verify("-340282346638528859811704183484516925440", -0x1.fffffep+127f); + verify("-1e-999", -0.0f, std::errc::result_out_of_range); verify("1." "175494140627517859246175898662808184331245864732796240031385942718174" @@ -1727,3 +1733,328 @@ TEST_CASE("float.decimal_point") { "96875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); } + +#ifdef __STDCPP_FLOAT16_T__ +TEST_CASE("float16.inf") { + verify("INF", std::numeric_limits::infinity()); + verify("-INF", -std::numeric_limits::infinity()); + verify("INFINITY", std::numeric_limits::infinity()); + verify("-INFINITY", -std::numeric_limits::infinity()); + verify("infinity", std::numeric_limits::infinity()); + verify("-infinity", -std::numeric_limits::infinity()); + verify("inf", std::numeric_limits::infinity()); + verify("-inf", -std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", + std::numeric_limits::infinity(), + std::errc::result_out_of_range); + verify("2e3000", std::numeric_limits::infinity(), + std::errc::result_out_of_range); + verify("3.5028234666e38", std::numeric_limits::infinity(), + std::errc::result_out_of_range); +} + +TEST_CASE("float16.general") { + // max + verify("65504", 0x1.ffcp+15f16); + // -max + verify("-65504", -0x1.ffcp+15f16); + // min + verify("0.000060975551605224609375", 0x1.ff8p-15f16); + verify("6.0975551605224609375e-5", 0x1.ff8p-15f16); + // denorm_min + verify("0.000000059604644775390625", 0x1p-24f16); + verify("5.9604644775390625e-8", 0x1p-24f16); + // -min + verify("-0.000060975551605224609375", -0x1.ff8p-15f16); + verify("-6.0975551605224609375e-5", -0x1.ff8p-15f16); + // -denorm_min + verify("-0.000000059604644775390625", -0x1p-24f16); + verify("-5.9604644775390625e-8", -0x1p-24f16); + + verify("-1e-999", -0.0f16, std::errc::result_out_of_range); + verify("6.0975551605224609375", 0x1.864p+2f16); + verify_runtime(append_zeros("6.0975551605224609375", 655), 0x1.864p+2f16); + verify_runtime(append_zeros("6.0975551605224609375", 656), 0x1.864p+2f16); + verify_runtime(append_zeros("6.0975551605224609375", 1000), 0x1.864p+2f16); + verify_runtime(append_zeros("6.0975551605224609375", 655) + + std::string("e-5"), + 0x1.ff8p-15f16); + verify_runtime(append_zeros("6.0975551605224609375", 656) + + std::string("e-5"), + 0x1.ff8p-15f16); + verify_runtime(append_zeros("6.0975551605224609375", 1000) + + std::string("e-5"), + 0x1.ff8p-15f16); + verify("-0", -0.0f16); + // verify("1090544144181609348835077142190", 0x1.b877ap+99f16); + // verify("1.1754943508e-38", 1.1754943508e-38f16); + verify("30219.0830078125", 30219.0830078125f16); + verify("17419.6494140625", 17419.6494140625f16); + verify("15498.36376953125", 15498.36376953125f16); + verify("6318.580322265625", 6318.580322265625f16); + verify("2525.2840576171875", 2525.2840576171875f16); + verify("1370.9265747070312", 1370.9265747070312f16); + verify("936.3702087402344", 936.3702087402344f16); + verify("411.88682556152344", 411.88682556152344f16); + verify("206.50310516357422", 206.50310516357422f16); + verify("124.16878890991211", 124.16878890991211f16); + verify("50.811574935913086", 50.811574935913086f16); + verify("17.486443519592285", 17.486443519592285f16); + verify("13.91745138168335", 13.91745138168335f16); + verify("7.5464513301849365", 0x1.e2f90ep+2f16); + verify("2.687217116355896", 2.687217116355896f16); + verify("1.1877630352973938", 0x1.30113ep+0f16); + verify("0.7622503340244293", 0.7622503340244293f16); + verify("0.30531780421733856", 0x1.38a53ap-2f16); + verify("0.21791061013936996", 0x1.be47eap-3f16); + verify("0.09289376810193062", 0x1.7c7e2ep-4f16); + verify("0.03706067614257336", 0.03706067614257336f16); + verify("0.028068351559340954", 0.028068351559340954f16); + verify("0.012114629615098238", 0x1.8cf8e2p-7f16); + verify("0.004221370676532388", 0x1.14a6dap-8f16); + verify("0.002153817447833717", 0.002153817447833717f16); + verify("0.0015924838953651488", 0x1.a175cap-10f16); + verify("0.0008602388261351734", 0.0008602388261351734f16); + verify("0.00036393293703440577", 0x1.7d9c82p-12f16); + verify("0.00013746770127909258", 0.00013746770127909258f16); + verify("16407.9462890625", 16407.9462890625f16); + // verify("1.1754947011469036e-38", 0x1.000006p-126f16); + // verify("7.0064923216240854e-46", 0x1p-149f16); + // verify("8388614.5", 8388614.5f16); + verify("0e9999999999999999999999999999", 0.f16); + // verify( + // "4.7019774032891500318749461488889827112746622270883500860350068251e-38", + // 4.7019774032891500318749461488889827112746622270883500860350068251e-38f16); + verify( + "3." + "141592653589793238462643383279502884197169399375105820974944592307816406" + "2862089986280348253421170679", + 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f16); + // verify( + // "2.3509887016445750159374730744444913556373311135441750430175034126e-38", + // 2.3509887016445750159374730744444913556373311135441750430175034126e-38f16); + verify("1", 1.f16); + // verify("7.0060e-46", 0.f16, std::errc::result_out_of_range); + // verify("3.4028234664e38", 0x1.fffffep+127f16); + // verify("3.4028234665e38", 0x1.fffffep+127f16); + // verify("3.4028234666e38", 0x1.fffffep+127f16); + // verify( + // "0." + // "000000000000000000000000000000000000011754943508222875079687365372222456" + // "778186655567720875215087517062784172594547271728515625", + // 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625f16); + // verify( + // "0." + // "000000000000000000000000000000000000000000001401298464324817070923729583" + // "289916131280261941876515771757068283889791082685860601486638188362121582" + // "03125", + // 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f16); + // verify( + // "0." + // "000000000000000000000000000000000000023509885615147285834557659820715330" + // "266457179855179808553659262368500061299303460771170648513361811637878417" + // "96875", + // 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f16); + // verify( + // "0." + // "000000000000000000000000000000000000011754942106924410754870294448492873" + // "488270524287458933338571745305715888704756189042655023513361811637878417" + // "96875", + // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f16); +} +#endif + +#ifdef __STDCPP_BFLOAT16_T__ +TEST_CASE("bfloat16.inf") { + verify("INF", std::numeric_limits::infinity()); + verify("-INF", -std::numeric_limits::infinity()); + verify("INFINITY", std::numeric_limits::infinity()); + verify("-INFINITY", -std::numeric_limits::infinity()); + verify("infinity", std::numeric_limits::infinity()); + verify("-infinity", -std::numeric_limits::infinity()); + verify("inf", std::numeric_limits::infinity()); + verify("-inf", -std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", + std::numeric_limits::infinity(), + std::errc::result_out_of_range); + verify("2e3000", std::numeric_limits::infinity(), + std::errc::result_out_of_range); + verify("3.5028234666e38", std::numeric_limits::infinity(), + std::errc::result_out_of_range); +} + +TEST_CASE("bfloat16.general") { + // max + verify("338953138925153547590470800371487866880", 0x1.fep+127bf16); + // -max + verify("-338953138925153547590470800371487866880", -0x1.fep+127bf16); + // min + verify( + "0." + "000000000000000000000000000000000000011754943508222875079687365372222456" + "778186655567720875215087517062784172594547271728515625", + 0x1p-126bf16); + verify("1." + "175494350822287507968736537222245677818665556772087521508751706278417" + "2594" + "547271728515625e-38", + 0x1p-126bf16); + // denorm_min + verify("0." + "000000000000000000000000000000000000000091835496157991211560057541970" + "4879" + "435795832466228193376178712270530013483949005603790283203125", + 0x1p-133bf16); + verify("9." + "183549615799121156005754197048794357958324662281933761787122705300134" + "8394" + "9005603790283203125e-41", + 0x1p-133bf16); + // -min + verify( + "-0." + "000000000000000000000000000000000000011754943508222875079687365372222456" + "778186655567720875215087517062784172594547271728515625", + -0x1p-126bf16); + verify( + "-1." + "175494350822287507968736537222245677818665556772087521508751706278417259" + "4547271728515625e-38", + -0x1p-126bf16); + // -denorm_min + verify("-0" + ".00000000000000000000000000000000000000009183549615799121156005754197" + "0487" + "9435795832466228193376178712270530013483949005603790283203125", + -0x1p-133bf16); + verify("-9" + ".18354961579912115600575419704879435795832466228193376178712270530013" + "4839" + "49005603790283203125e-41", + -0x1p-133bf16); + + verify("-1e-999", -0.0bf16, std::errc::result_out_of_range); + verify_runtime( + "1." + "175494350822287507968736537222245677818665556772087521508751706278417" + "2594547271728515625", + 0x1.2cp+0bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 655), + 0x1.2cp+0bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 656), + 0x1.2cp+0bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 1000), + 0x1.2cp+0bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 655) + + std::string("e-38"), + 0x1p-126bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 656) + + std::string("e-38"), + 0x1p-126bf16); + verify_runtime(append_zeros("1." + "175494350822287507968736537222245677818665556772" + "0875215087517062784172594547271728515625", + 1000) + + std::string("e-38"), + 0x1p-126bf16); + verify("-0", -0.0bf16); + // verify("1090544144181609348835077142190", 0x1.b877ap+99bf16); + // verify("1.1754943508e-38", 1.1754943508e-38bf16); + verify("30219.0830078125", 30219.0830078125bf16); + verify("16252921.5", 16252921.5bf16); + verify("5322519.25", 5322519.25bf16); + verify("3900245.875", 3900245.875bf16); + verify("1510988.3125", 1510988.3125bf16); + verify("782262.28125", 782262.28125bf16); + verify("328381.484375", 328381.484375bf16); + verify("156782.0703125", 156782.0703125bf16); + verify("85003.24609375", 85003.24609375bf16); + verify("17419.6494140625", 17419.6494140625bf16); + verify("15498.36376953125", 15498.36376953125bf16); + verify("6318.580322265625", 6318.580322265625bf16); + verify("2525.2840576171875", 2525.2840576171875bf16); + verify("1370.9265747070312", 1370.9265747070312bf16); + verify("936.3702087402344", 936.3702087402344bf16); + verify("411.88682556152344", 411.88682556152344bf16); + verify("206.50310516357422", 206.50310516357422bf16); + verify("124.16878890991211", 124.16878890991211bf16); + verify("50.811574935913086", 50.811574935913086bf16); + verify("17.486443519592285", 17.486443519592285bf16); + verify("13.91745138168335", 13.91745138168335bf16); + verify("7.5464513301849365", 0x1.e2f90ep+2bf16); + verify("2.687217116355896", 2.687217116355896bf16); + verify("1.1877630352973938", 0x1.30113ep+0bf16); + verify("0.7622503340244293", 0.7622503340244293bf16); + verify("0.30531780421733856", 0x1.38a53ap-2bf16); + verify("0.21791061013936996", 0x1.be47eap-3bf16); + verify("0.09289376810193062", 0x1.7c7e2ep-4bf16); + verify("0.03706067614257336", 0.03706067614257336bf16); + verify("0.028068351559340954", 0.028068351559340954bf16); + verify("0.012114629615098238", 0x1.8cf8e2p-7bf16); + verify("0.004221370676532388", 0x1.14a6dap-8bf16); + verify("0.002153817447833717", 0.002153817447833717bf16); + verify("0.0015924838953651488", 0x1.a175cap-10bf16); + verify("0.0008602388261351734", 0.0008602388261351734bf16); + verify("0.00036393293703440577", 0x1.7d9c82p-12bf16); + verify("0.00013746770127909258", 0.00013746770127909258bf16); + verify("16407.9462890625", 16407.9462890625bf16); + // verify("1.1754947011469036e-38", 0x1.000006p-126bf16); + // verify("7.0064923216240854e-46", 0x1p-149bf16); + // verify("8388614.5", 8388614.5bf16); + verify("0e9999999999999999999999999999", 0.bf16); + // verify( + // "4.7019774032891500318749461488889827112746622270883500860350068251e-38", + // 4.7019774032891500318749461488889827112746622270883500860350068251e-38bf16); + verify( + "3." + "141592653589793238462643383279502884197169399375105820974944592307816406" + "2862089986280348253421170679", + 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679bf16); + // verify( + // "2.3509887016445750159374730744444913556373311135441750430175034126e-38", + // 2.3509887016445750159374730744444913556373311135441750430175034126e-38bf16); + verify("1", 1.bf16); + verify("7.0060e-46", 0.bf16, std::errc::result_out_of_range); + verify("3.388e+38", 0x1.fep+127bf16); + verify("3.389e+38", 0x1.fep+127bf16); + verify("3.390e+38", 0x1.fep+127bf16); + // verify( + // "0." + // "000000000000000000000000000000000000011754943508222875079687365372222456" + // "778186655567720875215087517062784172594547271728515625", + // 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625bf16); + // verify( + // "0." + // "000000000000000000000000000000000000000000001401298464324817070923729583" + // "289916131280261941876515771757068283889791082685860601486638188362121582" + // "03125", + // 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125bf16); + // verify( + // "0." + // "000000000000000000000000000000000000023509885615147285834557659820715330" + // "266457179855179808553659262368500061299303460771170648513361811637878417" + // "96875", + // 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875bf16); + // verify( + // "0." + // "000000000000000000000000000000000000011754942106924410754870294448492873" + // "488270524287458933338571745305715888704756189042655023513361811637878417" + // "96875", + // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16); +} +#endif From 63bbefad6b69adfbc1aa9c5302b20c8fe861a931 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Tue, 3 Dec 2024 23:47:21 +0100 Subject: [PATCH 12/20] templates and types --- tests/basictest.cpp | 53 ++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 2513be5..f62d46f 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -56,15 +56,18 @@ #define FASTFLOAT_ODDPLATFORM 1 #endif -#define iHexAndDec(v) \ - (std::ostringstream{} << std::hex << "0x" << (v) << " (" << std::dec << (v) \ - << ")") \ - .str() -#define fHexAndDec(v) \ - (std::ostringstream{} << std::hexfloat << (v) << " (" << std::defaultfloat \ - << std::setprecision(DBL_MAX_10_EXP + 1) << (v) \ - << ")") \ - .str() +template std::string iHexAndDec(T v) { + std::ostringstream ss; + ss << std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"; + return ss.str(); +} + +template std::string fHexAndDec(T v) { + std::ostringstream ss; + ss << std::hexfloat << (v) << " (" << std::defaultfloat + << std::setprecision(DBL_MAX_10_EXP + 1) << (v) << ")"; + return ss.str(); +} char const *round_name(int d) { switch (d) { @@ -174,7 +177,7 @@ TEST_CASE("double.parse_zero") { auto r1 = fast_float::from_chars(zero, zero + 1, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -184,7 +187,7 @@ TEST_CASE("double.parse_zero") { auto r2 = fast_float::from_chars(zero, zero + 1, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -194,7 +197,7 @@ TEST_CASE("double.parse_zero") { auto r3 = fast_float::from_chars(zero, zero + 1, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -204,7 +207,7 @@ TEST_CASE("double.parse_zero") { auto r4 = fast_float::from_chars(zero, zero + 1, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -226,7 +229,7 @@ TEST_CASE("double.parse_negative_zero") { CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -237,7 +240,7 @@ TEST_CASE("double.parse_negative_zero") { CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -248,7 +251,7 @@ TEST_CASE("double.parse_negative_zero") { CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -259,7 +262,7 @@ TEST_CASE("double.parse_negative_zero") { CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; @@ -312,7 +315,7 @@ TEST_CASE("float.parse_zero") { auto r1 = fast_float::from_chars(zero, zero + 1, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -322,7 +325,7 @@ TEST_CASE("float.parse_zero") { auto r2 = fast_float::from_chars(zero, zero + 1, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -332,7 +335,7 @@ TEST_CASE("float.parse_zero") { auto r3 = fast_float::from_chars(zero, zero + 1, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -342,7 +345,7 @@ TEST_CASE("float.parse_zero") { auto r4 = fast_float::from_chars(zero, zero + 1, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -364,7 +367,7 @@ TEST_CASE("float.parse_negative_zero") { CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -375,7 +378,7 @@ TEST_CASE("float.parse_negative_zero") { CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -386,7 +389,7 @@ TEST_CASE("float.parse_negative_zero") { CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; @@ -397,7 +400,7 @@ TEST_CASE("float.parse_negative_zero") { CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) << std::endl; - CHECK(f == 0); + CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; From 62f526dfd32deb5b091b1ed5ee9efe02512a8ba0 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Tue, 3 Dec 2024 23:52:57 +0100 Subject: [PATCH 13/20] fix -Werror=conversion --- include/fast_float/float_common.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index f819fee..8a1e6c3 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -1067,8 +1067,10 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void to_float(bool negative, adjusted_mantissa am, T &value) { using equiv_uint = equiv_uint_t; equiv_uint word = equiv_uint(am.mantissa); - word |= equiv_uint(am.power2) << binary_format::mantissa_explicit_bits(); - word |= equiv_uint(negative) << binary_format::sign_index(); + word = equiv_uint(word | equiv_uint(am.power2) + << binary_format::mantissa_explicit_bits()); + word = + equiv_uint(word | equiv_uint(negative) << binary_format::sign_index()); #if FASTFLOAT_HAS_BIT_CAST value = std::bit_cast(word); #else From baaf58d2dd28d55b1f1ed7c27974169fa297498a Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 00:13:20 +0100 Subject: [PATCH 14/20] fix -Werror=maybe-uninitialized --- tests/basictest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index f62d46f..f341fc2 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -986,7 +986,7 @@ constexpr void check_basic_test_result(stringtype str, result_type result, template constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) { - T actual; + T actual{}; auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); check_basic_test_result(str, result, actual, expected, expected_ec); @@ -1018,7 +1018,7 @@ constexpr void basic_test(std::string_view str, T expected, template constexpr void basic_test(std::string_view str, T expected, fast_float::parse_options options) { - T actual; + T actual{}; auto result = fast_float::from_chars_advanced( str.data(), str.data() + str.size(), actual, options); check_basic_test_result(str, result, actual, expected, std::errc()); @@ -1028,7 +1028,7 @@ template constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec, fast_float::parse_options options) { - T actual; + T actual{}; auto result = fast_float::from_chars_advanced( str.data(), str.data() + str.size(), actual, options); check_basic_test_result(str, result, actual, expected, expected_ec); From f23ced2e4e47a17a5e81ffdeb83976d912f37873 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 01:02:20 +0100 Subject: [PATCH 15/20] fix for supplemental --- tests/basictest.cpp | 48 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index f341fc2..fd3c70a 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -433,28 +433,28 @@ bool check_file(std::string file_name) { if (str.size() > 0) { #ifdef __STDCPP_FLOAT16_T__ // Read 16-bit hex - uint16_t float16; + 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"; + std::cerr << "16-bit parsing failure: " << str << "\n"; return false; } #endif // Read 32-bit hex - uint32_t float32; + uint32_t float32{}; auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(), float32, 16); if (r32.ec != std::errc()) { - std::cerr << "32-bit parsing failure\n"; + std::cerr << "32-bit parsing failure: " << str << "\n"; return false; } // Read 64-bit hex - uint64_t float64; + uint64_t float64{}; auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(), float64, 16); if (r64.ec != std::errc()) { - std::cerr << "64-bit parsing failure\n"; + std::cerr << "64-bit parsing failure: " << str << "\n"; return false; } // The string to parse: @@ -467,34 +467,34 @@ bool check_file(std::string file_name) { 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"; + std::cerr << "16-bit fast_float parsing failure: " << str << "\n"; return false; } #endif // Parse as 32-bit float - float parsed_32; + float parsed_32{}; auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32); if (fast_float_r32.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { - std::cerr << "32-bit fast_float parsing failure for: " + str + "\n"; + std::cerr << "32-bit fast_float parsing failure: " << str << "\n"; return false; } // Parse as 64-bit float - double parsed_64; + double parsed_64{}; auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64); if (fast_float_r64.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { - std::cerr << "64-bit fast_float parsing failure: " + str + "\n"; + std::cerr << "64-bit fast_float parsing failure: " << str << "\n"; return false; } // Convert the floats to unsigned ints. #ifdef __STDCPP_FLOAT16_T__ - uint16_t float16_parsed; + uint16_t float16_parsed{}; #endif - uint32_t float32_parsed; - uint64_t float64_parsed; + uint32_t float32_parsed{}; + uint64_t float64_parsed{}; #ifdef __STDCPP_FLOAT16_T__ ::memcpy(&float16_parsed, &parsed_16, sizeof(parsed_16)); @@ -504,9 +504,9 @@ bool check_file(std::string file_name) { // 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 + std::cout << "bad 16: " << str << std::endl; + std::cout << "parsed as " << fHexAndDec(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); @@ -514,17 +514,17 @@ bool check_file(std::string file_name) { } #endif if (float32_parsed != float32) { - std::cout << "bad 32 " << str << std::endl; - std::cout << "parsed as " << iHexAndDec(parsed_32) << std::endl; - std::cout << "as raw uint32_t, parsed = " << float32_parsed + std::cout << "bad 32: " << str << std::endl; + std::cout << "parsed as " << fHexAndDec(parsed_32) << std::endl; + std::cout << "as raw uint32_t, parsed = " << float32_parsed << ", expected = " << float32 << std::endl; std::cout << "fesetround: " << round_name(d) << std::endl; fesetround(FE_TONEAREST); return false; } if (float64_parsed != float64) { - std::cout << "bad 64 " << str << std::endl; - std::cout << "parsed as " << iHexAndDec(parsed_64) << std::endl; + std::cout << "bad 64: " << str << std::endl; + std::cout << "parsed as " << fHexAndDec(parsed_64) << std::endl; std::cout << "as raw uint64_t, parsed = " << float64_parsed << ", expected = " << float64 << std::endl; std::cout << "fesetround: " << round_name(d) << std::endl; @@ -550,7 +550,9 @@ bool check_file(std::string file_name) { TEST_CASE("supplemental") { std::string path = SUPPLEMENTAL_TEST_DATA_DIR; for (auto const &entry : std::filesystem::directory_iterator(path)) { - CHECK(check_file(entry.path().string())); + const auto file = entry.path().string(); + CAPTURE(file); + CHECK(check_file(file)); } } #endif From 2a6c2604b5489933c6f7644e8c331207c5944d9d Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 09:34:34 +0100 Subject: [PATCH 16/20] check for IEEE 754 compliance --- include/fast_float/float_common.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 8a1e6c3..8947ce2 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -1258,30 +1258,46 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { static_assert(std::is_same, uint64_t>::value, "equiv_uint should be uint64_t for double"); +static_assert(std::numeric_limits::is_iec559, + "double must fulfill the requirements of IEC 559 (IEEE 754)"); static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for float"); +static_assert(std::numeric_limits::is_iec559, + "float must fulfill the requirements of IEC 559 (IEEE 754)"); #ifdef __STDCPP_FLOAT64_T__ static_assert(std::is_same, uint64_t>::value, "equiv_uint should be uint64_t for std::float64_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif #ifdef __STDCPP_FLOAT32_T__ static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for std::float32_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif #ifdef __STDCPP_FLOAT16_T__ static_assert( std::is_same::equiv_uint, uint16_t>::value, "equiv_uint should be uint16_t for std::float16_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif #ifdef __STDCPP_BFLOAT16_T__ static_assert( std::is_same::equiv_uint, uint16_t>::value, "equiv_uint should be uint16_t for std::bfloat16_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif constexpr chars_format operator~(chars_format rhs) noexcept { From 7226c004909950806f2c7d20fc605150d14ad870 Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 09:35:32 +0100 Subject: [PATCH 17/20] cleanup --- include/fast_float/float_common.h | 114 ------------------------------ 1 file changed, 114 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 8947ce2..e717a88 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -260,90 +260,6 @@ struct is_supported_char_type > { }; -#if 0 -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__ -#endif // 0 - // Compares two ASCII strings in a case insensitive manner. template inline FASTFLOAT_CONSTEXPR14 bool @@ -1078,36 +994,6 @@ to_float(bool negative, adjusted_mantissa am, T &value) { #endif } -#if 0 -#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__ -#endif // 0 - template struct space_lut { static constexpr bool value[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, From ce274f7051e40dc98c177a35e2fe7babe47f1aca Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 09:40:06 +0100 Subject: [PATCH 18/20] include and order types largerst to smallest --- include/fast_float/float_common.h | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index e717a88..926fea9 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #ifdef __has_include @@ -222,13 +223,13 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { template struct is_supported_float_type : std::integral_constant< - bool, std::is_same::value || std::is_same::value -#ifdef __STDCPP_FLOAT32_T__ - || std::is_same::value -#endif + bool, std::is_same::value || std::is_same::value #ifdef __STDCPP_FLOAT64_T__ || std::is_same::value #endif +#ifdef __STDCPP_FLOAT32_T__ + || std::is_same::value +#endif #ifdef __STDCPP_FLOAT16_T__ || std::is_same::value #endif @@ -757,8 +758,7 @@ template <> inline constexpr size_t binary_format::max_digits() { return 22; } - -#endif +#endif // __STDCPP_FLOAT16_T__ // credit: Jakub Jelínek #ifdef __STDCPP_BFLOAT16_T__ @@ -881,8 +881,7 @@ template <> inline constexpr size_t binary_format::max_digits() { return 98; } - -#endif +#endif // __STDCPP_BFLOAT16_T__ template <> inline constexpr uint64_t @@ -1051,7 +1050,6 @@ template <> constexpr char32_t const *str_const_nan() { template <> constexpr char8_t const *str_const_nan() { return u8"nan"; } - #endif template constexpr UC const *str_const_inf(); @@ -1074,7 +1072,6 @@ template <> constexpr char32_t const *str_const_inf() { template <> constexpr char8_t const *str_const_inf() { return u8"infinity"; } - #endif template struct int_luts { @@ -1158,7 +1155,7 @@ static_assert(std::is_same, uint64_t>::value, static_assert( std::numeric_limits::is_iec559, "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); -#endif +#endif // __STDCPP_FLOAT64_T__ #ifdef __STDCPP_FLOAT32_T__ static_assert(std::is_same, uint32_t>::value, @@ -1166,7 +1163,7 @@ static_assert(std::is_same, uint32_t>::value, static_assert( std::numeric_limits::is_iec559, "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); -#endif +#endif // __STDCPP_FLOAT32_T__ #ifdef __STDCPP_FLOAT16_T__ static_assert( @@ -1175,7 +1172,7 @@ static_assert( static_assert( std::numeric_limits::is_iec559, "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)"); -#endif +#endif // __STDCPP_FLOAT16_T__ #ifdef __STDCPP_BFLOAT16_T__ static_assert( @@ -1184,7 +1181,7 @@ static_assert( static_assert( std::numeric_limits::is_iec559, "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)"); -#endif +#endif // __STDCPP_BFLOAT16_T__ constexpr chars_format operator~(chars_format rhs) noexcept { using int_type = std::underlying_type::type; From dc39efa49d8cee8f193094fc54ba6cfb02970a0c Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 21:03:11 +0100 Subject: [PATCH 19/20] readme fix for skip_white_space and allow_leading_plus --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22d1cc8..5bb3e27 100644 --- a/README.md +++ b/README.md @@ -107,9 +107,9 @@ The library seeks to follow the C++17 (see [28.2.3.(6.1)](https://eel.is/c++draft/charconv.from.chars#6.1)) specification. * The `from_chars` function does not skip leading white-space characters (unless - `fast_float::chars_format::chars_format` is set). + `fast_float::chars_format::skip_white_space` is set). * [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is - forbidden (unless `fast_float::chars_format::skip_white_space` is set). + forbidden (unless `fast_float::chars_format::allow_leading_plus` is set). * It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point From 87aae63ed7183ae8beb9e69ac0d788ec0e334d4e Mon Sep 17 00:00:00 2001 From: Anders Dalvander Date: Wed, 4 Dec 2024 21:12:25 +0100 Subject: [PATCH 20/20] readme updates for std::float16_t and std::bfloat16_t --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5bb3e27..8bfd6cf 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,8 @@ The library seeks to follow the C++17 (see Furthermore, we have the following restrictions: * We support `float` and `double`, but not `long double`. We also support - fixed-width floating-point types such as `std::float32_t` and - `std::float64_t`. + fixed-width floating-point types such as `std::float64_t`, `std::float32_t`, + `std::float16_t`, and `std::bfloat16_t`. * We only support the decimal format: we do not support hexadecimal strings. * For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned @@ -241,7 +241,8 @@ constexpr double constexptest() { ## C++23: Fixed width floating-point types The library also supports fixed-width floating-point types such as -`std::float32_t` and `std::float64_t`. E.g., you can write: +`std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`. +E.g., you can write: ```C++ std::float32_t result;