diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index 4b12169..5cd9b7d 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -217,6 +217,12 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } else if (*next == exp_char || *next == capital_exp_char) { + // Would be a number without a significand e.g. e+03 + if (next == first) + { + return {next, EINVAL}; + } + ++next; if (fmt == chars_format::fixed) { @@ -243,25 +249,33 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } from_chars_result r {}; - if (fmt == chars_format::hex) - { - r = from_chars(significand_buffer, significand_buffer + offset, significand, 16); - } - else - { - r = from_chars(significand_buffer, significand_buffer + offset, significand); - } - switch (r.ec) - { - case EINVAL: - return {first, EINVAL}; - case ERANGE: - return {next, ERANGE}; - } - if (round) + // If the significand is 0 from chars will return EINVAL because there is nothing in the buffer, + // but it is a valid value. We need to continue parsing to get the correct value of ptr even + // though we know we could bail now. + // + // See GitHub issue #29: https://github.com/cppalliance/charconv/issues/29 + if (offset != 0) { - significand += 1; + if (fmt == chars_format::hex) + { + r = from_chars(significand_buffer, significand_buffer + offset, significand, 16); + } else + { + r = from_chars(significand_buffer, significand_buffer + offset, significand); + } + switch (r.ec) + { + case EINVAL: + return {first, EINVAL}; + case ERANGE: + return {next, ERANGE}; + } + + if (round) + { + significand += 1; + } } } diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index 8b95a2e..678d9f3 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -139,7 +139,11 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& auto r = boost::charconv::detail::parser(first, last, sign, significand, exponent, fmt); if (r.ec != 0) { - value = 0; + return r; + } + else if (significand == 0) + { + value = sign ? static_cast(-0.0L) : static_cast(0.0L); return r; } diff --git a/test/from_chars_float.cpp b/test/from_chars_float.cpp index dbcbed2..5bc5c25 100644 --- a/test/from_chars_float.cpp +++ b/test/from_chars_float.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include template @@ -167,6 +168,10 @@ void odd_strings_test() BOOST_TEST_EQ(r5.ec, 0); BOOST_TEST_EQ(v5, static_cast(1.23456789123456789123456789123456789123456789e-5L)); + const char* buffer6 = "E01"; + T v6 = 0; + auto r6 = boost::charconv::from_chars(buffer6, buffer6 + std::strlen(buffer6), v6); + BOOST_TEST_EQ(r6.ec, EINVAL); } template diff --git a/test/test_boost_json_values.cpp b/test/test_boost_json_values.cpp index d416b49..10e8ec4 100644 --- a/test/test_boost_json_values.cpp +++ b/test/test_boost_json_values.cpp @@ -363,5 +363,15 @@ int main() "00000000000000000000000000000000000000000000000000" // 500 zeroes ); + // Reported in issue #29 + // https://github.com/cppalliance/charconv/issues/29 + + fc("0E0"); + fc("0E01"); + fc("0.0e0"); + fc("-0E0"); + fc("-0E01"); + fc("-0.0e0"); + return boost::report_errors(); }