diff --git a/CMakeLists.txt b/CMakeLists.txt index a86dfc2..645fce7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) -project(fast_float VERSION 8.0.2 LANGUAGES CXX) +project(fast_float VERSION 8.1.0 LANGUAGES CXX) set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat") set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) option(FASTFLOAT_TEST "Enable tests" OFF) diff --git a/README.md b/README.md index fb56985..17e7b12 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,33 @@ int main() { } ``` +## Multiplication of an integer by a power of 10 +An integer `W` can be multiplied by a power of ten `10^Q` and +converted to `double` with correctly rounded value +(in "round to nearest, tie to even" fashion) using +`fast_float::integer_times_pow10()`, e.g.: +```C++ +const uint64_t W = 12345678901234567; +const int Q = 23; +const double result = fast_float::integer_times_pow10(W, Q); +std::cout.precision(17); +std::cout << W << " * 10^" << Q << " = " << result << " (" + << (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n"; +``` +outputs +``` +12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected) +``` +`fast_float::integer_times_pow10()` gives the same result as +using `fast_float::from_chars()` when parsing the string `"WeQ"` +(in this example `"12345678901234567e23"`), +except `fast_float::integer_times_pow10()` does not report out-of-range errors, and +underflows to zero or overflows to infinity when the resulting value is +out of range. + +Overloads of `fast_float::integer_times_pow10()` are provided for +signed and unsigned integer types: `int64_t`, `uint64_t`, etc. + You also can use not standard options: ```C++ @@ -426,6 +453,8 @@ The fast_float library is part of: * GCC (as of version 12): the `from_chars` function in GCC relies on fast_float, * [Chromium](https://github.com/Chromium/Chromium), the engine behind Google Chrome, Microsoft Edge, and Opera, +* Boost JSON, MySQL, etc. +* Blender * [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser), * [DuckDB](https://duckdb.org), @@ -528,7 +557,7 @@ sufficiently recent version of CMake (3.11 or better at least): FetchContent_Declare( fast_float GIT_REPOSITORY https://github.com/fastfloat/fast_float.git - GIT_TAG tags/v8.0.2 + GIT_TAG tags/v8.1.0 GIT_SHALLOW TRUE) FetchContent_MakeAvailable(fast_float) @@ -544,7 +573,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so: CPMAddPackage( NAME fast_float GITHUB_REPOSITORY "fastfloat/fast_float" - GIT_TAG v8.0.2) + GIT_TAG v8.1.0) ``` ## Using as single header @@ -556,7 +585,7 @@ if desired as described in the command line help. You may directly download automatically generated single-header files: - + ## Benchmarking diff --git a/include/fast_float/fast_float.h b/include/fast_float/fast_float.h index 4ca1a0a..1d13815 100644 --- a/include/fast_float/fast_float.h +++ b/include/fast_float/fast_float.h @@ -45,6 +45,24 @@ FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t const &options) noexcept; +/** + * This function multiplies an integer number by a power of 10 and returns + * the result as a double precision floating-point value that is correctly + * rounded. The resulting floating-point value is the closest floating-point + * value, using the "round to nearest, tie to even" convention for values that + * would otherwise fall right in-between two values. That is, we provide exact + * conversion according to the IEEE standard. + * + * On overflow infinity is returned, on underflow 0 is returned. + * + * The implementation does not throw and does not allocate memory (e.g., with + * `new` or `malloc`). + */ +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(uint64_t const mantissa, int const decimal_exponent) noexcept; +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(int64_t const mantissa, int const decimal_exponent) noexcept; + /** * from_chars for integer types. */ diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 7c9d367..72e36ef 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -16,8 +16,8 @@ #include "constexpr_feature_detect.h" #define FASTFLOAT_VERSION_MAJOR 8 -#define FASTFLOAT_VERSION_MINOR 0 -#define FASTFLOAT_VERSION_PATCH 2 +#define FASTFLOAT_VERSION_MINOR 1 +#define FASTFLOAT_VERSION_PATCH 0 #define FASTFLOAT_STRINGIZE_IMPL(x) #x #define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) @@ -1202,7 +1202,12 @@ template constexpr uint64_t int_luts::min_safe_u64[]; template fastfloat_really_inline constexpr uint_fast8_t ch_to_digit(UC c) noexcept { - return int_luts<>::chdigit[static_cast(c)]; + // wchar_t and char can be signed, so we need to be careful. + using UnsignedUC = typename std::make_unsigned::type; + return int_luts<>::chdigit[static_cast( + static_cast(c) & + static_cast( + -((static_cast(c) & ~0xFFull) == 0)))]; } fastfloat_really_inline constexpr uint_fast8_t diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 306c735..af08b80 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -195,32 +195,21 @@ from_chars(UC const *first, UC const *last, T &value, parse_options_t(fmt)); } -/** - * This function overload takes parsed_number_string_t structure that is created - * and populated either by from_chars_advanced function taking chars range and - * parsing options or other parsing custom function implemented by user. - */ -template -FASTFLOAT_CONSTEXPR20 from_chars_result_t -from_chars_advanced(parsed_number_string_t const &pns, T &value) noexcept { +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +clinger_fast_path_impl(uint64_t const mantissa, int64_t const exponent, +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + bool const is_negative, +#endif + T &value) noexcept { - static_assert(is_supported_float_type::value, - "only some floating-point types are supported"); - static_assert(is_supported_char_type::value, - "only char, wchar_t, char16_t and char32_t are supported"); - - from_chars_result_t answer; - - answer.ec = std::errc(); // be optimistic - answer.ptr = pns.lastmatch; // The implementation of the Clinger's fast path is convoluted because // we want round-to-nearest in all cases, irrespective of the rounding mode // selected on the thread. // We proceed optimistically, assuming that detail::rounds_to_nearest() // returns true. - if (binary_format::min_exponent_fast_path() <= pns.exponent && - pns.exponent <= binary_format::max_exponent_fast_path() && - !pns.too_many_digits) { + if (binary_format::min_exponent_fast_path() <= exponent && + exponent <= binary_format::max_exponent_fast_path()) { // Unfortunately, the conventional Clinger's fast path is only possible // when the system rounds to the nearest float. // @@ -233,37 +222,36 @@ from_chars_advanced(parsed_number_string_t const &pns, T &value) noexcept { #endif // We have that fegetround() == FE_TONEAREST. // Next is Clinger's fast path. - if (pns.mantissa <= binary_format::max_mantissa_fast_path()) { - value = T(pns.mantissa); - if (pns.exponent < 0) { - value = value / binary_format::exact_power_of_ten(-pns.exponent); + if (mantissa <= binary_format::max_mantissa_fast_path()) { + value = T(mantissa); + if (exponent < 0) { + value = value / binary_format::exact_power_of_ten(-exponent); } else { - value = value * binary_format::exact_power_of_ten(pns.exponent); + value = value * binary_format::exact_power_of_ten(exponent); } #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN - if (pns.negative) { + if (is_negative) { value = -value; } #endif - return answer; + return true; } #ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED } else { // We do not have that fegetround() == FE_TONEAREST. // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's // proposal - if (pns.exponent >= 0 && - pns.mantissa <= - binary_format::max_mantissa_fast_path(pns.exponent)) { + if (exponent >= 0 && + mantissa <= binary_format::max_mantissa_fast_path(exponent)) { #if defined(__clang__) || defined(FASTFLOAT_32BIT) // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD - if (pns.mantissa == 0) { + if (mantissa == 0) { value = #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN - pns.negative ? T(-0.) : + is_negative ? T(-0.) : #endif T(0.); - return answer; + return true; } #endif value = T(pns.mantissa) * @@ -273,11 +261,40 @@ from_chars_advanced(parsed_number_string_t const &pns, T &value) noexcept { value = -value; } #endif - return answer; + return true; } } #endif } + return false; +} + +/** + * This function overload takes parsed_number_string_t structure that is created + * and populated either by from_chars_advanced function taking chars range and + * parsing options or other parsing custom function implemented by user. + */ +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { + static_assert(is_supported_float_type::value, + "only some floating-point types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + from_chars_result_t answer; + + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + + if (!pns.too_many_digits && + clinger_fast_path_impl(pns.mantissa, pns.exponent, +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + pns.negative, +#endif + value)) + return answer; + adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); if (pns.too_many_digits && am.power2 >= 0) { @@ -369,6 +386,69 @@ from_chars(UC const *first, UC const *last, T &value, int const base) noexcept { return from_chars_advanced(first, last, value, options); } +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(uint64_t const mantissa, int const decimal_exponent) noexcept { + double value; + if (clinger_fast_path_impl(mantissa, decimal_exponent, +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + false, +#endif + value)) + return value; + + adjusted_mantissa am = + compute_float>(decimal_exponent, mantissa); + to_float( +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + false, +#endif + am, value); + return value; +} + +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(int64_t const mantissa, int const decimal_exponent) noexcept { +#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + FASTFLOAT_ASSUME(mantissa > 0); + const uint64_t m = static_cast(mantissa); +#else + const bool is_negative = mantissa < 0; + const uint64_t m = static_cast(is_negative ? -mantissa : mantissa); +#endif + double value; + if (clinger_fast_path_impl(m, decimal_exponent, +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + is_negative, +#endif + value)) + return value; + + adjusted_mantissa am = + compute_float>(decimal_exponent, m); + to_float( +#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN + is_negative, +#endif + am, value); + return value; +} + +// the following overloads are here to avoid surprising ambiguity for int, +// unsigned, etc. +template +FASTFLOAT_CONSTEXPR20 inline typename std::enable_if< + std::is_integral::value && !std::is_signed::value, double>::type +integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(static_cast(mantissa), decimal_exponent); +} + +template +FASTFLOAT_CONSTEXPR20 inline typename std::enable_if< + std::is_integral::value && std::is_signed::value, double>::type +integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(static_cast(mantissa), decimal_exponent); +} + template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_int_advanced(UC const *first, UC const *last, T &value, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 36925f0..d8ed6f4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -61,6 +61,7 @@ fast_float_add_cpp_test(wide_char_test) fast_float_add_cpp_test(supported_chars_test) fast_float_add_cpp_test(example_test) fast_float_add_cpp_test(example_comma_test) +fast_float_add_cpp_test(example_integer_times_pow10) fast_float_add_cpp_test(basictest) option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF) if (FASTFLOAT_CONSTEXPR_TESTS) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 552d6f1..dc11752 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1134,6 +1134,14 @@ TEST_CASE("double.inf") { std::errc::result_out_of_range); verify("1.9e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); + + // DBL_MAX + 0.00000000000000001e308 + verify("1.79769313486231581e308", std::numeric_limits::infinity(), + std::errc::result_out_of_range); + + // DBL_MAX + 0.0000000000000001e308 + verify("1.7976931348623159e308", std::numeric_limits::infinity(), + std::errc::result_out_of_range); } TEST_CASE("double.general") { @@ -1143,6 +1151,13 @@ TEST_CASE("double.general") { verify("-22250738585072012e-324", -0x1p-1022); /* limit between normal and subnormal*/ verify("-1e-999", -0.0, std::errc::result_out_of_range); + + // DBL_TRUE_MIN / 2 + verify("2.4703282292062327e-324", 0.0, std::errc::result_out_of_range); + + // DBL_TRUE_MIN / 2 + 0.0000000000000001e-324 + verify("2.4703282292062328e-324", 0x0.0000000000001p-1022); + verify("-2.2222222222223e-322", -0x1.68p-1069); verify("9007199254740993.0", 0x1p+53); verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); @@ -2070,3 +2085,155 @@ TEST_CASE("bfloat16.general") { // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16); } #endif + +template +void verify_integer_multiplication_by_power_of_10(Int mantissa, + int decimal_exponent, + double expected) { + const double actual = + fast_float::integer_times_pow10(mantissa, decimal_exponent); + + INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent + << "\n" + " expected=" + << fHexAndDec(expected) << "\n" + << " ..actual=" << fHexAndDec(actual) << "\n" + << " expected mantissa=" + << iHexAndDec(get_mantissa(expected)) << "\n" + << " ..actual mantissa=" << iHexAndDec(get_mantissa(actual)) + << "\n"); + CHECK_EQ(actual, expected); +} + +template +void verify_integer_multiplication_by_power_of_10(Int mantissa, + int decimal_exponent) { + std::string constructed_string = + std::to_string(mantissa) + "e" + std::to_string(decimal_exponent); + double expected_result; + const auto result = fast_float::from_chars( + constructed_string.data(), + constructed_string.data() + constructed_string.size(), expected_result); + if (result.ec != std::errc()) + INFO("Failed to parse: " << constructed_string); + verify_integer_multiplication_by_power_of_10(mantissa, decimal_exponent, + expected_result); +} + +TEST_CASE("integer_times_pow10") { + // explicitly verifying API with different types of integers + verify_integer_multiplication_by_power_of_10(31, -1, 3.1); + verify_integer_multiplication_by_power_of_10(-31, -1, -3.1); + verify_integer_multiplication_by_power_of_10(31, -1, 3.1); + verify_integer_multiplication_by_power_of_10(31415, -4, 3.1415); + verify_integer_multiplication_by_power_of_10(-31415, -4, -3.1415); + verify_integer_multiplication_by_power_of_10(31415, -4, 3.1415); + verify_integer_multiplication_by_power_of_10(314159265, -8, + 3.14159265); + verify_integer_multiplication_by_power_of_10(-314159265, -8, + -3.14159265); + verify_integer_multiplication_by_power_of_10(3141592653, -9, + 3.141592653); + verify_integer_multiplication_by_power_of_10( + 3141592653589793238, -18, 3.141592653589793238); + verify_integer_multiplication_by_power_of_10( + -3141592653589793238, -18, -3.141592653589793238); + verify_integer_multiplication_by_power_of_10( + 3141592653589793238, -18, 3.141592653589793238); + verify_integer_multiplication_by_power_of_10( + -3141592653589793238, -18, -3.141592653589793238); + verify_integer_multiplication_by_power_of_10( + 3141592653589793238, -18, 3.141592653589793238); + + for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) { + fesetround(mode); + INFO("fesetround(): " << std::string{round_name(mode)}); + + struct Guard { + ~Guard() { fesetround(FE_TONEAREST); } + } guard; + + verify_integer_multiplication_by_power_of_10(0, 0); + verify_integer_multiplication_by_power_of_10(1, 0); + verify_integer_multiplication_by_power_of_10(0, 1); + verify_integer_multiplication_by_power_of_10(1, 1); + verify_integer_multiplication_by_power_of_10(-1, 0); + verify_integer_multiplication_by_power_of_10(0, -1); + verify_integer_multiplication_by_power_of_10(-1, -1); + verify_integer_multiplication_by_power_of_10(-1, 1); + verify_integer_multiplication_by_power_of_10(1, -1); + + verify_integer_multiplication_by_power_of_10( + 49406564584124654, -340, std::numeric_limits::denorm_min()); + verify_integer_multiplication_by_power_of_10( + 22250738585072014, -324, std::numeric_limits::min()); + verify_integer_multiplication_by_power_of_10( + 17976931348623158, 292, std::numeric_limits::max()); + + // DBL_TRUE_MIN / 2 underflows to 0 + verify_integer_multiplication_by_power_of_10(49406564584124654 / 2, -340, + 0.); + + // DBL_TRUE_MIN / 2 + 0.0000000000000001e-324 rounds to DBL_TRUE_MIN + verify_integer_multiplication_by_power_of_10( + 49406564584124654 / 2 + 1, -340, + std::numeric_limits::denorm_min()); + + // DBL_MAX + 0.0000000000000001e308 overflows to infinity + verify_integer_multiplication_by_power_of_10( + 17976931348623158 + 1, 292, std::numeric_limits::infinity()); + // DBL_MAX + 0.00000000000000001e308 overflows to infinity + verify_integer_multiplication_by_power_of_10( + 179769313486231580 + 1, 291, std::numeric_limits::infinity()); + + // loosely verifying correct rounding of 1 to 64 bits + // worth of significant digits + verify_integer_multiplication_by_power_of_10(1, 42); + verify_integer_multiplication_by_power_of_10(1, -42); + verify_integer_multiplication_by_power_of_10(12, 42); + verify_integer_multiplication_by_power_of_10(12, -42); + verify_integer_multiplication_by_power_of_10(123, 42); + verify_integer_multiplication_by_power_of_10(123, -42); + verify_integer_multiplication_by_power_of_10(1234, 42); + verify_integer_multiplication_by_power_of_10(1234, -42); + verify_integer_multiplication_by_power_of_10(12345, 42); + verify_integer_multiplication_by_power_of_10(12345, -42); + verify_integer_multiplication_by_power_of_10(123456, 42); + verify_integer_multiplication_by_power_of_10(123456, -42); + verify_integer_multiplication_by_power_of_10(1234567, 42); + verify_integer_multiplication_by_power_of_10(1234567, -42); + verify_integer_multiplication_by_power_of_10(12345678, 42); + verify_integer_multiplication_by_power_of_10(12345678, -42); + verify_integer_multiplication_by_power_of_10(123456789, 42); + verify_integer_multiplication_by_power_of_10(1234567890, 42); + verify_integer_multiplication_by_power_of_10(1234567890, -42); + verify_integer_multiplication_by_power_of_10(12345678901, 42); + verify_integer_multiplication_by_power_of_10(12345678901, -42); + verify_integer_multiplication_by_power_of_10(123456789012, 42); + verify_integer_multiplication_by_power_of_10(123456789012, -42); + verify_integer_multiplication_by_power_of_10(1234567890123, 42); + verify_integer_multiplication_by_power_of_10(1234567890123, -42); + verify_integer_multiplication_by_power_of_10(12345678901234, 42); + verify_integer_multiplication_by_power_of_10(12345678901234, -42); + verify_integer_multiplication_by_power_of_10(123456789012345, 42); + verify_integer_multiplication_by_power_of_10(123456789012345, -42); + verify_integer_multiplication_by_power_of_10(1234567890123456, 42); + verify_integer_multiplication_by_power_of_10(1234567890123456, -42); + verify_integer_multiplication_by_power_of_10(12345678901234567, 42); + verify_integer_multiplication_by_power_of_10(12345678901234567, -42); + verify_integer_multiplication_by_power_of_10(123456789012345678, 42); + verify_integer_multiplication_by_power_of_10(123456789012345678, -42); + verify_integer_multiplication_by_power_of_10(1234567890123456789, 42); + verify_integer_multiplication_by_power_of_10(1234567890123456789, -42); + verify_integer_multiplication_by_power_of_10(12345678901234567890ull, 42); + verify_integer_multiplication_by_power_of_10(12345678901234567890ull, -42); + verify_integer_multiplication_by_power_of_10( + std::numeric_limits::max(), 42); + verify_integer_multiplication_by_power_of_10( + std::numeric_limits::max(), -42); + verify_integer_multiplication_by_power_of_10( + std::numeric_limits::max(), 42); + verify_integer_multiplication_by_power_of_10( + std::numeric_limits::max(), -42); + } +} \ No newline at end of file diff --git a/tests/example_integer_times_pow10.cpp b/tests/example_integer_times_pow10.cpp new file mode 100644 index 0000000..3e86826 --- /dev/null +++ b/tests/example_integer_times_pow10.cpp @@ -0,0 +1,12 @@ +#include "fast_float/fast_float.h" + +#include + +int main() { + const uint64_t W = 12345678901234567; + const int Q = 23; + const double result = fast_float::integer_times_pow10(W, Q); + std::cout.precision(17); + std::cout << W << " * 10^" << Q << " = " << result << " (" + << (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n"; +} diff --git a/tests/fast_int.cpp b/tests/fast_int.cpp index 9b107c8..49044d3 100644 --- a/tests/fast_int.cpp +++ b/tests/fast_int.cpp @@ -831,6 +831,275 @@ int main() { return EXIT_FAILURE; } } + // dont parse UTF-16 code units of emojis as int if low byte is ascii digit + { + const std::u16string emojis[] = { + u"ℹ", u"ℹ️", u"☸", u"☸️", u"☹", u"☹️", u"✳", u"✳️", + u"✴", u"✴️", u"⤴", u"⤴️", u"⤵", u"⤵️", u"〰", u"〰️", + }; + bool failed = false; + auto array_size = sizeof(emojis) / sizeof(emojis[0]); + for (size_t i = 0; i < array_size; i++) { + auto e = emojis[i]; + int foo; + auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo); + if (answer.ec == std::errc()) { + failed = true; + std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo + << "." << std::endl; + } + } + + if (failed) { + return EXIT_FAILURE; + } + } + // dont parse UTF-32 code points of emojis as int if low byte is ascii digit + { + const std::u32string emojis[] = { + U"ℹ", + U"ℹ️", + U"☸", + U"☸️", + U"☹", + U"☹️", + U"✳", + U"✳️", + U"✴", + U"✴️", + U"⤴", + U"⤴️", + U"⤵", + U"⤵️", + U"〰", + U"〰️", + U"🈲", + U"🈳", + U"🈴", + U"🈵", + U"🈶", + U"🈷", + U"🈷️", + U"🈸", + U"🈹", + U"🌰", + U"🌱", + U"🌲", + U"🌳", + U"🌴", + U"🌵", + U"🌶", + U"🌶️", + U"🌷", + U"🌸", + U"🌹", + U"🐰", + U"🐱", + U"🐲", + U"🐳", + U"🐴", + U"🐵", + U"🐶", + U"🐷", + U"🐸", + U"🐹", + U"🔰", + U"🔱", + U"🔲", + U"🔳", + U"🔴", + U"🔵", + U"🔶", + U"🔷", + U"🔸", + U"🔹", + U"😰", + U"😱", + U"😲", + U"😳", + U"😴", + U"😵", + U"😵‍💫", + U"😶", + U"😶‍🌫", + U"😶‍🌫️", + U"😷", + U"😸", + U"😹", + U"🤰", + U"🤰🏻", + U"🤰🏼", + U"🤰🏽", + U"🤰🏾", + U"🤰🏿", + U"🤱", + U"🤱🏻", + U"🤱🏼", + U"🤱🏽", + U"🤱🏾", + U"🤱🏿", + U"🤲", + U"🤲🏻", + U"🤲🏼", + U"🤲🏽", + U"🤲🏾", + U"🤲🏿", + U"🤳", + U"🤳🏻", + U"🤳🏼", + U"🤳🏽", + U"🤳🏾", + U"🤳🏿", + U"🤴", + U"🤴🏻", + U"🤴🏼", + U"🤴🏽", + U"🤴🏾", + U"🤴🏿", + U"🤵", + U"🤵‍♀", + U"🤵‍♀️", + U"🤵‍♂", + U"🤵‍♂️", + U"🤵🏻", + U"🤵🏻‍♀", + U"🤵🏻‍♀️", + U"🤵🏻‍♂", + U"🤵🏻‍♂️", + U"🤵🏼", + U"🤵🏼‍♀", + U"🤵🏼‍♀️", + U"🤵🏼‍♂", + U"🤵🏼‍♂️", + U"🤵🏽", + U"🤵🏽‍♀", + U"🤵🏽‍♀️", + U"🤵🏽‍♂", + U"🤵🏽‍♂️", + U"🤵🏾", + U"🤵🏾‍♀", + U"🤵🏾‍♀️", + U"🤵🏾‍♂", + U"🤵🏾‍♂️", + U"🤵🏿", + U"🤵🏿‍♀", + U"🤵🏿‍♀️", + U"🤵🏿‍♂", + U"🤵🏿‍♂️", + U"🤶", + U"🤶🏻", + U"🤶🏼", + U"🤶🏽", + U"🤶🏾", + U"🤶🏿", + U"🤷", + U"🤷‍♀", + U"🤷‍♀️", + U"🤷‍♂", + U"🤷‍♂️", + U"🤷🏻", + U"🤷🏻‍♀", + U"🤷🏻‍♀️", + U"🤷🏻‍♂", + U"🤷🏻‍♂️", + U"🤷🏼", + U"🤷🏼‍♀", + U"🤷🏼‍♀️", + U"🤷🏼‍♂", + U"🤷🏼‍♂️", + U"🤷🏽", + U"🤷🏽‍♀", + U"🤷🏽‍♀️", + U"🤷🏽‍♂", + U"🤷🏽‍♂️", + U"🤷🏾", + U"🤷🏾‍♀", + U"🤷🏾‍♀️", + U"🤷🏾‍♂", + U"🤷🏾‍♂️", + U"🤷🏿", + U"🤷🏿‍♀", + U"🤷🏿‍♀️", + U"🤷🏿‍♂", + U"🤷🏿‍♂️", + U"🤸", + U"🤸‍♀", + U"🤸‍♀️", + U"🤸‍♂", + U"🤸‍♂️", + U"🤸🏻", + U"🤸🏻‍♀", + U"🤸🏻‍♀️", + U"🤸🏻‍♂", + U"🤸🏻‍♂️", + U"🤸🏼", + U"🤸🏼‍♀", + U"🤸🏼‍♀️", + U"🤸🏼‍♂", + U"🤸🏼‍♂️", + U"🤸🏽", + U"🤸🏽‍♀", + U"🤸🏽‍♀️", + U"🤸🏽‍♂", + U"🤸🏽‍♂️", + U"🤸🏾", + U"🤸🏾‍♀", + U"🤸🏾‍♀️", + U"🤸🏾‍♂", + U"🤸🏾‍♂️", + U"🤸🏿", + U"🤸🏿‍♀", + U"🤸🏿‍♀️", + U"🤸🏿‍♂", + U"🤸🏿‍♂️", + U"🤹", + U"🤹‍♀", + U"🤹‍♀️", + U"🤹‍♂", + U"🤹‍♂️", + U"🤹🏻", + U"🤹🏻‍♀", + U"🤹🏻‍♀️", + U"🤹🏻‍♂", + U"🤹🏻‍♂️", + U"🤹🏼", + U"🤹🏼‍♀", + U"🤹🏼‍♀️", + U"🤹🏼‍♂", + U"🤹🏼‍♂️", + U"🤹🏽", + U"🤹🏽‍♀", + U"🤹🏽‍♀️", + U"🤹🏽‍♂", + U"🤹🏽‍♂️", + U"🤹🏾", + U"🤹🏾‍♀", + U"🤹🏾‍♀️", + U"🤹🏾‍♂", + U"🤹🏾‍♂️", + U"🤹🏿", + U"🤹🏿‍♀", + U"🤹🏿‍♀️", + U"🤹🏿‍♂", + U"🤹🏿‍♂️", + }; + bool failed = false; + auto array_size = sizeof(emojis) / sizeof(emojis[0]); + for (size_t i = 0; i < array_size; i++) { + auto e = emojis[i]; + int foo; + auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo); + if (answer.ec == std::errc()) { + failed = true; + std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo + << "." << std::endl; + } + } + + if (failed) { + return EXIT_FAILURE; + } + } return EXIT_SUCCESS; } @@ -842,4 +1111,4 @@ int main() { std::cerr << "The test requires C++17." << std::endl; return EXIT_SUCCESS; } -#endif \ No newline at end of file +#endif