diff --git a/.drone.jsonnet b/.drone.jsonnet index 88bc18af..c0c03792 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -349,7 +349,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") = windows_pipeline( "Windows VS2015 msvc-14.0", "cppalliance/dronevs2015", - { TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' }, + { TOOLSET: 'msvc-14.0', CXXSTD: '14,latest', B2_DONT_EMBED_MANIFEST: '1' }, ), windows_pipeline( diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b455b93f..715586f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,13 @@ jobs: os: ubuntu-22.04 install: - g++-12-multilib + - toolset: gcc-12 + cxxstd: "03,11,14,17,20,23" + address_model: 32,64 + os: ubuntu-22.04 + install: + - g++-12-multilib + define: "BOOST_CHARCONV_STD_ERANGE" - toolset: gcc-12 cxxstd: "03-gnu,11-gnu,14-gnu,17-gnu,20-gnu,23-gnu" address_model: 32,64 @@ -454,6 +461,10 @@ jobs: then B2_ARGS+=("address-model=${{matrix.address_model}}") fi + if [ -n "${{matrix.define}}" ] + then + B2_ARGS+=("define=${{matrix.define}}") + fi B2_ARGS+=("libs/$LIBRARY/test") ./b2 "${B2_ARGS[@]}" diff --git a/doc/charconv/from_chars.adoc b/doc/charconv/from_chars.adoc index c34abb6a..e12432d8 100644 --- a/doc/charconv/from_chars.adoc +++ b/doc/charconv/from_chars.adoc @@ -57,6 +57,7 @@ from_chars_result from_chars(const char* first, const char* last, Real& value, c === from_chars for floating point types * On std::errc::result_out_of_range we return ±0 for small values (e.g. 1.0e-99999) or ±HUGE_VAL for large values (e.g. 1.0e+99999) to match the handling of `std::strtod`. This is a divergence from the standard which states we should return the `value` argument unmodified. +** If you want the behavior from the standard, compile the library with `BOOST_CHARCONV_STD_ERANGE` defined. * These functions have been tested to support all built-in floating-point types and those from C++23's `` ** Long doubles can be 64, 80, or 128-bit, but must be IEEE 754 compliant. An example of a non-compliant, and therefore unsupported, format is `__ibm128`. ** Use of `__float128` or `std::float128_t` requires compiling with `-std=gnu++xx` and linking GCC's `libquadmath`. diff --git a/src/from_chars.cpp b/src/from_chars.cpp index ef13feae..b99ac674 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -33,20 +33,52 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first, const char* last, float& value, boost::charconv::chars_format fmt) noexcept { + #ifdef BOOST_CHARCONV_STD_ERANGE + + float temp_value; + const auto r = fmt != boost::charconv::chars_format::hex ? boost::charconv::detail::fast_float::from_chars(first, last, temp_value, fmt) : + boost::charconv::detail::from_chars_float_impl(first, last, temp_value, fmt); + if (r) + { + value = temp_value; + } + + return r; + + #else + if (fmt != boost::charconv::chars_format::hex) { return boost::charconv::detail::fast_float::from_chars(first, last, value, fmt); } return boost::charconv::detail::from_chars_float_impl(first, last, value, fmt); + + #endif } boost::charconv::from_chars_result boost::charconv::from_chars(const char* first, const char* last, double& value, boost::charconv::chars_format fmt) noexcept { + #ifdef BOOST_CHARCONV_STD_ERANGE + + double temp_value; + const auto r = fmt != boost::charconv::chars_format::hex ? boost::charconv::detail::fast_float::from_chars(first, last, temp_value, fmt) : + boost::charconv::detail::from_chars_float_impl(first, last, temp_value, fmt); + if (r) + { + value = temp_value; + } + + return r; + + #else + if (fmt != boost::charconv::chars_format::hex) { return boost::charconv::detail::fast_float::from_chars(first, last, value, fmt); } return boost::charconv::detail::from_chars_float_impl(first, last, value, fmt); + + #endif } #ifdef BOOST_CHARCONV_HAS_FLOAT128 @@ -79,7 +111,11 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first auto return_val = boost::charconv::detail::compute_float128(exponent, significand, sign, success); r.ec = static_cast(success); + #ifdef BOOST_CHARCONV_STD_ERANGE + if (r.ec == std::errc()) + #else if (r.ec == std::errc() || r.ec == std::errc::result_out_of_range) + #endif { value = return_val; } @@ -202,7 +238,11 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first auto return_val = boost::charconv::detail::compute_float80(exponent, significand, sign, success); r.ec = success; + #ifdef BOOST_CHARCONV_STD_ERANGE + if (r.ec == std::errc()) + #else if (r.ec == std::errc() || r.ec == std::errc::result_out_of_range) + #endif { value = return_val; } diff --git a/test/Jamfile b/test/Jamfile index b642d562..613ef60c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -54,3 +54,4 @@ run from_chars_float2.cpp ; run-fail STL_benchmark.cpp : : : [ requires cxx17_hdr_charconv ] [ check-target-builds ../config//has_double_conversion "Google double-coversion support" : "double-conversion" ] ; run test_float128.cpp : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : "quadmath" ] ; run P2497.cpp ; +run github_issue_110.cpp ; diff --git a/test/from_chars_float.cpp b/test/from_chars_float.cpp index 8237f74b..c7e0dc96 100644 --- a/test/from_chars_float.cpp +++ b/test/from_chars_float.cpp @@ -393,6 +393,14 @@ void boost_json_test() fc("6372891218502368041059e064"); } +// Issue 37 conflicts with handling from 110 +#ifdef BOOST_CHARCONV_STD_ERANGE + +template +void test_issue_37() {} + +#else + template void test_issue_37() { @@ -422,6 +430,8 @@ void test_issue_37() overflow_spot_value("-1.0e-99999", static_cast(-0.0L)); } +#endif + template void test_issue_45(T v, const std::string& full_buffer, const std::ptrdiff_t ptr, boost::charconv::chars_format fmt = boost::charconv::chars_format::general) { diff --git a/test/github_issue_110.cpp b/test/github_issue_110.cpp new file mode 100644 index 00000000..f86a4bba --- /dev/null +++ b/test/github_issue_110.cpp @@ -0,0 +1,66 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#ifdef BOOST_CHARCONV_STD_ERANGE + +template +void overflow_spot_value(const std::string& buffer, boost::charconv::chars_format fmt = boost::charconv::chars_format::general) +{ + auto v = static_cast(42.L); + auto r = boost::charconv::from_chars(buffer.c_str(), buffer.c_str() + std::strlen(buffer.c_str()), v, fmt); + + if (!(BOOST_TEST_EQ(v, static_cast(42.L)) && BOOST_TEST(r.ec == std::errc::result_out_of_range))) + { + std::cerr << "Test failure for: " << buffer << " got: " << v << std::endl; + } +} + +template +void test() +{ + const auto format_list = {boost::charconv::chars_format::general, boost::charconv::chars_format::scientific, boost::charconv::chars_format::hex}; + + for (const auto format : format_list) + { + if (format != boost::charconv::chars_format::hex) + { + overflow_spot_value("1e99999", format); + overflow_spot_value("-1e99999", format); + overflow_spot_value("1e-99999", format); + overflow_spot_value("-1.0e-99999", format); + } + else + { + overflow_spot_value("1p99999", format); + overflow_spot_value("-1p99999", format); + overflow_spot_value("1p-99999", format); + overflow_spot_value("-1.0p-99999", format); + } + } +} + +int main() +{ + test(); + test(); + test(); + + #ifdef BOOST_CHARCONV_HAS_FLOAT128 + test<__float128>(); + #endif + + return boost::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif diff --git a/test/test_boost_json_values.cpp b/test/test_boost_json_values.cpp index 56379423..52dc4645 100644 --- a/test/test_boost_json_values.cpp +++ b/test/test_boost_json_values.cpp @@ -234,6 +234,16 @@ void test_within_ulp() } }; +// Directly conflicts with the expected outcomes +#ifdef BOOST_CHARCONV_STD_ERANGE + +int main() +{ + return 0; +} + +#else + int main() { issue_599_test(); @@ -479,3 +489,5 @@ int main() return boost::report_errors(); } + +#endif