diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h index d40a230b884a04..b7d142394620ce 100644 --- a/libcxx/src/include/from_chars_floating_point.h +++ b/libcxx/src/include/from_chars_floating_point.h @@ -18,7 +18,6 @@ #include #include #include -#include #include // Included for the _Floating_type_traits class @@ -36,9 +35,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD // - __ptr is the current position is the input string. This is points beyond // the initial I character. // - __negative whether a valid string represents -inf or +inf. -template +template from_chars_result __from_chars_floating_point_inf( - const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) { + const char* const __first, const char* __last, _Fp& __value, const char* __ptr, bool __negative) { if (__last - __ptr < 2) [[unlikely]] return {__first, errc::invalid_argument}; @@ -60,11 +59,11 @@ from_chars_result __from_chars_floating_point_inf( && std::tolower(__ptr[4]) == 'y') __ptr += 5; - if constexpr (numeric_limits<_Tp>::has_infinity) { + if constexpr (numeric_limits<_Fp>::has_infinity) { if (__negative) - __value = -std::numeric_limits<_Tp>::infinity(); + __value = -std::numeric_limits<_Fp>::infinity(); else - __value = std::numeric_limits<_Tp>::infinity(); + __value = std::numeric_limits<_Fp>::infinity(); return {__ptr, std::errc{}}; } else { @@ -72,7 +71,7 @@ from_chars_result __from_chars_floating_point_inf( } } -// Parses an infinita string. +// Parses a nan string. // Valid strings are case insentitive and contain INF or INFINITY. // // - __first is the first argument to std::from_chars. When the string is invalid @@ -82,9 +81,9 @@ from_chars_result __from_chars_floating_point_inf( // - __ptr is the current position is the input string. This is points beyond // the initial N character. // - __negative whether a valid string represents -nan or +nan. -template +template from_chars_result __from_chars_floating_point_nan( - const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) { + const char* const __first, const char* __last, _Fp& __value, const char* __ptr, bool __negative) { if (__last - __ptr < 2) [[unlikely]] return {__first, errc::invalid_argument}; @@ -111,32 +110,33 @@ from_chars_result __from_chars_floating_point_nan( } if (__negative) - __value = -std::numeric_limits<_Tp>::quiet_NaN(); + __value = -std::numeric_limits<_Fp>::quiet_NaN(); else - __value = std::numeric_limits<_Tp>::quiet_NaN(); + __value = std::numeric_limits<_Fp>::quiet_NaN(); return {__ptr, std::errc{}}; } -template +template from_chars_result __from_chars_floating_point_decimal( const char* const __first, const char* __last, - _Tp& __value, + _Fp& __value, chars_format __fmt, const char* __ptr, bool __negative) { - using _Traits = _Floating_type_traits<_Tp>; + using _Traits = _Floating_type_traits<_Fp>; using _Uint_type = typename _Traits::_Uint_type; - ptrdiff_t length = __last - __first; - _LIBCPP_ASSERT_INTERNAL(length > 0, ""); - const char* src = __ptr; // rename to match the libc code copied for this section. + const char* src = __ptr; // rename to match the libc code copied for this section. + ptrdiff_t length = __last - src; + _LIBCPP_ASSERT_INTERNAL(length > 0, ""); _Uint_type mantissa = 0; int exponent = 0; bool truncated = false; bool seen_digit = false; + bool has_valid_exponent = false; bool after_decimal = false; size_t index = 0; const size_t BASE = 10; @@ -145,6 +145,7 @@ from_chars_result __from_chars_floating_point_decimal( // The loop fills the mantissa with as many digits as it can hold const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE; + while (index < static_cast(length)) { if (LIBC_NAMESPACE::internal::isdigit(src[index])) { uint32_t digit = src[index] - '0'; @@ -178,7 +179,7 @@ from_chars_result __from_chars_floating_point_decimal( } if (!seen_digit) - return {src + index, {}}; + return {__first, errc::invalid_argument}; if (index < static_cast(length) && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) { bool has_sign = false; @@ -187,8 +188,10 @@ from_chars_result __from_chars_floating_point_decimal( } if (index + 1 + static_cast(has_sign) < static_cast(length) && LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast(has_sign)])) { + has_valid_exponent = true; ++index; - auto result = LIBC_NAMESPACE::internal::strtointeger(src + index, 10); + auto result = + LIBC_NAMESPACE::internal::strtointeger(src + index, 10, static_cast(length) - index); // if (result.has_error()) // output.error = result.error; int32_t add_to_exponent = result.value; @@ -199,57 +202,102 @@ from_chars_result __from_chars_floating_point_decimal( // If the result is in the valid range, then we use it. The valid range is // also within the int32 range, so this prevents overflow issues. - if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) { - exponent = LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT; - } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) { - exponent = -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT; + if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT) { + exponent = LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT; + } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT) { + exponent = -LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT; } else { exponent = static_cast(temp_exponent); } } } - LIBC_NAMESPACE::internal::ExpandedFloat<_Tp> expanded_float = {0, 0}; + // [charconv.from.chars] + switch (__fmt) { + case chars_format::scientific: + // 6.2 if fmt has chars_format::scientific set but not chars_format::fixed, + // the otherwise optional exponent part shall appear; + if (!has_valid_exponent) + return {__first, errc::invalid_argument}; + break; + case chars_format::fixed: + // 6.3 if fmt has chars_format::fixed set but not chars_format::scientific, + // the optional exponent part shall not appear; + if (has_valid_exponent) + return {__first, errc::invalid_argument}; + break; + case chars_format::general: + case chars_format::hex: // impossible but it silences the compiler + break; + } + + LIBC_NAMESPACE::internal::ExpandedFloat<_Fp> expanded_float = {0, 0}; + errc status{}; if (mantissa != 0) { - auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Tp>( + auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Fp>( {mantissa, exponent}, truncated, LIBC_NAMESPACE::internal::RoundDirection::Nearest, src, length); expanded_float = temp.num; - // Note: there's also an error value in temp.error. I'm not doing that error handling right now though. + if (temp.error == ERANGE) { + status = errc::result_out_of_range; + } } - auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>(); + auto result = LIBC_NAMESPACE::fputil::FPBits<_Fp>(); result.set_mantissa(expanded_float.mantissa); result.set_biased_exponent(expanded_float.exponent); + + // C17 7.12.1/6 + // The result underflows if the magnitude of the mathematical result is so + // small that the mathematical re- sult cannot be represented, without + // extraordinary roundoff error, in an object of the specified type.237) If + // the result underflows, the function returns an implementation-defined + // value whose magnitude is no greater than the smallest normalized positive + // number in the specified type; if the integer expression math_errhandling + // & MATH_ERRNO is nonzero, whether errno acquires the value ERANGE is + // implementation-defined; if the integer expression math_errhandling & + // MATH_ERREXCEPT is nonzero, whether the "underflow" floating-point + // exception is raised is implementation-defined. + // + // LLLVM-LIBC sets ERAGNE for subnormal values + // + // [charconv.from.chars]/1 + // ... If the parsed value is not in the range representable by the type of + // value, value is unmodified and the member ec of the return value is + // equal to errc::result_out_of_range. ... + // + // Undo the ERANGE for subnormal values. + if (status == errc::result_out_of_range && result.is_subnormal() && !result.is_zero()) + status = errc{}; + if (__negative) __value = -result.get_val(); else __value = result.get_val(); - return {src + index, {}}; + + return {src + index, status}; } -template +template from_chars_result -__from_chars_floating_point(const char* const __first, const char* __last, _Tp& __value, chars_format __fmt) { +__from_chars_floating_point(const char* const __first, const char* __last, _Fp& __value, chars_format __fmt) { if (__first == __last) [[unlikely]] return {__first, errc::invalid_argument}; const char* __ptr = __first; - - // skip whitespace - while (std::isspace(*__ptr)) { - ++__ptr; - if (__ptr == __last) [[unlikely]] - return {__first, errc::invalid_argument}; // is this valid?? - } - - bool __negative = *__ptr == '-'; + bool __negative = *__ptr == '-'; if (__negative) { ++__ptr; if (__ptr == __last) [[unlikely]] return {__first, errc::invalid_argument}; } - if (!std::isdigit(*__ptr)) { + // [charconv.from.chars] + // [Note 1: If the pattern allows for an optional sign, but the string has + // no digit characters following the sign, no characters match the pattern. + // — end note] + // This is true for integrals, floating point allows -.0 + switch (std::tolower(*__ptr)) { + case 'i': // TODO Evaluate the other implementations // [charconv.from.chars]/6.2 // if fmt has chars_format::scientific set but not chars_format::fixed, @@ -259,20 +307,23 @@ __from_chars_floating_point(const char* const __first, const char* __last, _Tp& if (__fmt == chars_format::scientific) return {__first, errc::invalid_argument}; - switch (std::tolower(*__ptr)) { - case 'i': - return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative); - case 'n': - if constexpr (numeric_limits<_Tp>::has_quiet_NaN) - return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative); - [[fallthrough]]; - default: + return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative); + case 'n': + // TODO Evaluate the other implementations + // [charconv.from.chars]/6.2 + // if fmt has chars_format::scientific set but not chars_format::fixed, + // the otherwise optional exponent part shall appear; + // Since INF/NAN do not have an exponent this value is not valid. + // See LWG3456 + if (__fmt == chars_format::scientific) return {__first, errc::invalid_argument}; - } + if constexpr (numeric_limits<_Fp>::has_quiet_NaN) + return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative); + return {__first, errc::invalid_argument}; } #if 1 - _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, ""); + _LIBCPP_ASSERT_INTERNAL(__fmt != std::chars_format::hex, ""); #else if (__fmt == chars_format::hex) return std::__from_chars_floating_point_hex(__first, __last, __value); diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp index 99699c037b4615..4f55cdec77ba9c 100644 --- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp @@ -18,7 +18,10 @@ #include #include #include +#include #include +#include +#include #include "charconv_test_helpers.h" #include "test_macros.h" @@ -312,7 +315,7 @@ void test_fmt_independent(std::chars_format fmt) { assert(value == F(0.25)); } } - { // start with decimal separator + { // only decimal separator F value = 0.25; const char* s = "."; std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt); @@ -321,7 +324,16 @@ void test_fmt_independent(std::chars_format fmt) { assert(result.ptr == s); assert(value == F(0.25)); } - { // Invalid sign + { // sign and decimal separator + F value = 0.25; + const char* s = "-."; + std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt); + + assert(result.ec == std::errc::invalid_argument); + assert(result.ptr == s); + assert(value == F(0.25)); + } + { // + sign is not allowed F value = 0.25; const char* s = "+0.25"; std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt); @@ -335,19 +347,422 @@ void test_fmt_independent(std::chars_format fmt) { template struct test_basics { void operator()() { - for (auto fmt : - {std::chars_format::scientific, std::chars_format::fixed, std::chars_format::hex, std::chars_format::general}) + for (auto fmt : {std::chars_format::scientific, + std::chars_format::fixed, + /*std::chars_format::hex,*/ std::chars_format::general}) test_fmt_independent(fmt); } }; +template +struct test_fixed { + void operator()() { + std::from_chars_result r; + F x = 0.25; + + // *** Failures + + { // Starts with invalid character + std::array s = {' ', '1'}; + for (auto c : "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") { + s[0] = c; + r = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s.data()); + assert(x == F(0.25)); + } + } + + { // exponenent no sign + const char* s = "1.5e10"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // exponenent capitalized no sign + const char* s = "1.5E10"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // exponenent + sign + const char* s = "1.5e+10"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // exponenent - sign + const char* s = "1.5e-10"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // double exponent + const char* s = "1.25e0e12"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // Shifting mantissa exponent and an exponent + const char* s = "123.456e3"; + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + + // *** Success + + { // number followed by non-numeric values + const char* s = "001x"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(1.0)); + } + { // no leading digit + const char* s = ".5"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 2); + assert(x == F(0.5)); + } + { // negative sign and no leading digit + const char* s = "-.5"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(-0.5)); + } + + { // double deciamal point + const char* s = "1.25.78"; + + // This number is halfway between two float values. + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 4); + assert(x == F(1.25)); + } + { // Exponent no number + const char* s = "1.5e"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(1.5)); + } + { // Exponent sign no number + const char* s = "1.5e+"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(1.5)); + } + { // exponent double sign + const char* s = "1.25e++12"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 4); + assert(x == F(1.25)); + } + { // This number is halfway between two float values. + const char* s = "20040229"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 8); + assert(x == F(20040229)); + } + { // Shifting mantissa exponent and no exponent + const char* s = "123.456"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 7); + assert(x == F(1.23456e2)); + } + { // Mantissa overflow + { + const char* s = "0.111111111111111111111111111111111111111111"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(0.111111111111111111111111111111111111111111)); + } + { + const char* s = "111111111111.111111111111111111111111111111111111111111"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(111111111111.111111111111111111111111111111111111111111)); + } + } + { // Negative value + const char* s = "-0.25"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(-0.25)); + } + } +}; + +template +struct test_scientific { + void operator()() { + std::from_chars_result r; + F x = 0.25; + + // *** Failures + + { // Starts with invalid character + std::array s = {' ', '1', 'e', '0'}; + for (auto c : "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") { + s[0] = c; + r = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::scientific); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s.data()); + assert(x == F(0.25)); + } + } + { // No exponent + const char* s = "1.23"; + r = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // Exponent no number + const char* s = "1.23e"; + r = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific); + + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // Exponent sign no number + const char* s = "1.5e+"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + { // exponent double sign + const char* s = "1.25e++12"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s); + assert(x == F(0.25)); + } + + // *** Success + + { // number followed by non-numeric values + const char* s = "001e0x"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 5); + assert(x == F(1.0)); + } + + { // double deciamal point + const char* s = "1.25e0.78"; + + // This number is halfway between two float values. + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 6); + assert(x == F(1.25)); + } + + { // exponenent no sign + const char* s = "1.5e10"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 6); + assert(x == F(1.5e10)); + } + { // exponenent capitalized no sign + const char* s = "1.5E10"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 6); + assert(x == F(1.5e10)); + } + { // exponenent + sign + const char* s = "1.5e+10"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 7); + assert(x == F(1.5e10)); + } + { // exponenent - sign + const char* s = "1.5e-10"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 7); + assert(x == F(1.5e-10)); + } + { // double exponent + const char* s = "1.25e0e12"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 6); + assert(x == F(1.25)); + } + { // This number is halfway between two float values. + const char* s = "20040229e0"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 10); + assert(x == F(20040229)); + } + { // Shifting mantissa exponent and an exponent + const char* s = "123.456e3"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 9); + assert(x == F(1.23456e5)); + } + { // Mantissa overflow + { + const char* s = "0.111111111111111111111111111111111111111111e0"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(0.111111111111111111111111111111111111111111)); + } + { + const char* s = "111111111111.111111111111111111111111111111111111111111e0"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(111111111111.111111111111111111111111111111111111111111)); + } + } + { // Negative value + const char* s = "-0.25e0"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + std::strlen(s)); + assert(x == F(-0.25)); + } + { // value is too big -> +inf + const char* s = "1e9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == std::numeric_limits::infinity()); + } + { // negative value is too big -> -inf + const char* s = "-1e9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == -std::numeric_limits::infinity()); + } + { // value is too small -> 0 + const char* s = "1e-9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == F(0.0)); + } + { // negative value is too small -> -0 + const char* s = "-1e-9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == F(-0.0)); + } + } +}; + template struct test_general { void operator()() { std::from_chars_result r; - F x; + F x = 0.25; + + // *** Failures + + { // Starts with invalid character + std::array s = {' ', '1'}; + for (auto c : "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") { + s[0] = c; + r = std::from_chars(s.data(), s.data() + s.size(), x); - { // number followed by non-numeric valies + assert(r.ec == std::errc::invalid_argument); + assert(r.ptr == s.data()); + assert(x == F(0.25)); + } + } + + // *** Success + + { // number followed by non-numeric values const char* s = "001x"; // the expected form of the subject sequence is a nonempty sequence of @@ -359,7 +774,54 @@ struct test_general { assert(r.ptr == s + 3); assert(x == F(1.0)); } + { // no leading digit + const char* s = ".5e0"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 4); + assert(x == F(0.5)); + } + { // negative sign and no leading digit + const char* s = "-.5e0"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 5); + assert(x == F(-0.5)); + } + { // no leading digit + const char* s = ".5"; + + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 2); + assert(x == F(0.5)); + } + { // negative sign and no leading digit + const char* s = "-.5"; + // the expected form of the subject sequence is a nonempty sequence of + // decimal digits optionally containing a decimal-point character, then + // an optional exponent part as defined in 6.4.4.3, excluding any digit + // separators (6.4.4.2); (C23 7.24.1.5) + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(-0.5)); + } { // double deciamal point const char* s = "1.25.78"; @@ -369,7 +831,6 @@ struct test_general { assert(r.ptr == s + 4); assert(x == F(1.25)); } - { // exponenent no sign const char* s = "1.5e10"; @@ -378,6 +839,14 @@ struct test_general { assert(r.ptr == s + 6); assert(x == F(1.5e10)); } + { // exponenent capitalized no sign + const char* s = "1.5E10"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 6); + assert(x == F(1.5e10)); + } { // exponenent + sign const char* s = "1.5e+10"; @@ -394,6 +863,22 @@ struct test_general { assert(r.ptr == s + 7); assert(x == F(1.5e-10)); } + { // Exponent no number + const char* s = "1.5e"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(1.5)); + } + { // Exponent sign no number + const char* s = "1.5e+"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc{}); + assert(r.ptr == s + 3); + assert(x == F(1.5)); + } { // exponent double sign const char* s = "1.25e++12"; @@ -452,14 +937,6 @@ struct test_general { assert(x == F(111111111111.111111111111111111111111111111111111111111)); } } - { // Leading whitespace - const char* s = " \t\v\r\n0.25"; - - r = std::from_chars(s, s + std::strlen(s), x); - assert(r.ec == std::errc{}); - assert(r.ptr == s + std::strlen(s)); - assert(x == F(0.25)); - } { // Negative value const char* s = "-0.25"; @@ -468,12 +945,80 @@ struct test_general { assert(r.ptr == s + std::strlen(s)); assert(x == F(-0.25)); } + { // value is too big -> +inf + const char* s = "1e9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == std::numeric_limits::infinity()); + } + { // negative value is too big -> -inf + const char* s = "-1e9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == -std::numeric_limits::infinity()); + } + { // value is too small -> 0 + const char* s = "1e-9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == F(0.0)); + } + { // negative value is too small -> -0 + const char* s = "-1e-9999999999999999999999999999999999999999"; + + r = std::from_chars(s, s + std::strlen(s), x); + assert(r.ec == std::errc::result_out_of_range); + assert(r.ptr == s + strlen(s)); + assert(x == F(-0.0)); + } } }; +// The test +// test/std/utilities/charconv/charconv.msvc/test.cpp +// uses random values. This tests contains errors found by this test. +void test_random_errors() { + { + const char* s = "4.219902180869891e-2788"; + const char* last = s + std::strlen(s) - 1; + + // last + 1 contains a digit. When that value is parsed the exponent is + // e-2788 which returns std::errc::result_out_of_range and the value 0. + // the proper exponent is e-278, which can be represented by a double. + + double value = 0.25; + std::from_chars_result result = std::from_chars(s, last, value); + + assert(result.ec == std::errc{}); + assert(result.ptr == last); + assert(value == 4.219902180869891e-278); + } + { + const char* s = "7.411412e-39U"; + const char* last = s + std::strlen(s) - 1; + + float value = 0.25; + std::from_chars_result result = std::from_chars(s, last, value); + + assert(result.ec == std::errc{}); + assert(result.ptr == last); + assert(value == 7.411412e-39F); + } +} + int main(int, char**) { run(all_floats); + run(all_floats); + run(all_floats); run(all_floats); + test_random_errors(); + return 0; } diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp b/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp index c0fbfcb132f7ef..9fd5b0768442eb 100644 --- a/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp +++ b/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp @@ -121,7 +121,6 @@ inline constexpr FloatFromCharsTestCase float_from_chars_test_cases[] = { "2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222" "2222222222222222222e-45", chars_format::scientific, 1006, errc{}, 0x0.000004p-126f}, - // VSO-733765 ": [Feedback] double std::from_chars behavior on exponent out of range" // LWG-3081 "Floating point from_chars API does not distinguish between overflow and underflow" // These test cases exercise every overflow/underflow codepath. diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp index 30ee9adcd74bf0..a5218c6b6d7971 100644 --- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp @@ -27,6 +27,8 @@ #include #include +#include // TODO REMOVE + #include "double_fixed_precision_to_chars_test_cases_1.hpp" #include "double_fixed_precision_to_chars_test_cases_2.hpp" #include "double_fixed_precision_to_chars_test_cases_3.hpp" @@ -45,6 +47,7 @@ #include "float_hex_precision_to_chars_test_cases.hpp" #include "float_scientific_precision_to_chars_test_cases.hpp" #include "float_to_chars_test_cases.hpp" +#include "floating_point_test_cases.hpp" using namespace std; @@ -389,7 +392,22 @@ void test_from_chars(const string_view input, const BaseOrFmt base_or_fmt, const if constexpr (is_integral_v) { assert(mode == TestFromCharsMode::Normal); } - +#if 1 + else { + // Not implemented yet. + if (base_or_fmt == std::chars_format::hex) + return; + // LWG3456. Pattern used by std::from_chars is underspecified + if (base_or_fmt == std::chars_format::scientific && correct_ec == std::errc{} && opt_correct && + !std::isfinite(*opt_correct)) + // Based on the wording in the Standard scientific can't be NaN or Inf. + return; + if (base_or_fmt == std::chars_format::fixed && correct_ec == std::errc{} && + std::ranges::contains(input, 'e', [](char c) { return std::tolower(c); })) + // Fixed should not contain an exponent. MSVC terminates at e, libc++ marks as invalid. + return; + } +#endif constexpr T unmodified = 111; T dest = unmodified; @@ -589,8 +607,8 @@ void test_floating_prefix(const conditional_t(val) == bits, "round-trip", bits); -#endif +#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT } { @@ -786,8 +803,7 @@ void test_floating_prefixes(mt19937_64& mt64) { } } -// TODO Enable once std::from_chars has floating point support. -#if 0 +#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT template void test_floating_from_chars(const chars_format fmt) { test_from_chars("", fmt, 0, inv_arg); // no characters @@ -855,11 +871,20 @@ void test_floating_from_chars(const chars_format fmt) { // The UCRT considers indeterminate NaN to be negative quiet NaN with no payload bits set. // It parses "nan(ind)" and "-nan(ind)" identically. + // + // + // TODO Evaluate this code, probably needs to be commented out. + // + // +#if 0 test_from_chars("nan(InD)", fmt, 8, errc{}, -qnan); +#endif test_from_chars("-nan(InD)", fmt, 9, errc{}, -qnan); +#if 0 test_from_chars("nan(SnAn)", fmt, 9, errc{}, nullopt, TestFromCharsMode::SignalingNaN); test_from_chars("-nan(SnAn)", fmt, 10, errc{}, nullopt, TestFromCharsMode::SignalingNaN); +#endif switch (fmt) { case chars_format::general: @@ -941,7 +966,7 @@ void test_floating_from_chars(const chars_format fmt) { break; } } -#endif +#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT template void test_floating_to_chars( @@ -953,13 +978,11 @@ void test_floating_to_chars( void all_floating_tests(mt19937_64& mt64) { test_floating_prefixes(mt64); -// TODO Enable once std::from_chars has floating point support. -#if 0 +#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT for (const auto& fmt : {chars_format::general, chars_format::scientific, chars_format::fixed, chars_format::hex}) { test_floating_from_chars(fmt); test_floating_from_chars(fmt); } - // Test rounding. // See float_from_chars_test_cases.hpp in this directory. @@ -993,7 +1016,8 @@ void all_floating_tests(mt19937_64& mt64) { for (const auto& p : floating_point_test_cases_double) { test_from_chars(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast(p.second)); } -#endif +#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT + // See float_to_chars_test_cases.hpp in this directory. for (const auto& t : float_to_chars_test_cases) { if (t.fmt == chars_format{}) { diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp index 09ef70ea9924e8..190805687beb8c 100644 --- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp @@ -8,6 +8,8 @@ // UNSUPPORTED: c++03, c++11, c++14 +// ADDITIONAL_COMPILE_FLAGS: -O0 -g + // to_chars requires functions in the dylib that have not been introduced in older // versions of the dylib on macOS. // XFAIL: availability-fp_to_chars-missing @@ -22,6 +24,7 @@ // #include +#include "test_macros.h" // Work-around for sprintf_s's usage in the Microsoft tests. #ifndef _WIN32 diff --git a/libcxx/test/support/msvc_stdlib_force_include.h b/libcxx/test/support/msvc_stdlib_force_include.h index 785670224c3b18..6bfe6e39b711e9 100644 --- a/libcxx/test/support/msvc_stdlib_force_include.h +++ b/libcxx/test/support/msvc_stdlib_force_include.h @@ -103,5 +103,6 @@ const AssertionDialogAvoider assertion_dialog_avoider{}; #define TEST_ABI_MICROSOFT #define _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST +#define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT 1 #endif // SUPPORT_MSVC_STDLIB_FORCE_INCLUDE_H diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h index 15fc5b69b52076..07845458871ba2 100644 --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -260,6 +260,10 @@ #define TEST_IGNORE_NODISCARD (void) +#if defined(_LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT) && _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT +# define TEST_HAS_FROM_CHARS_FLOATING_POINT +#endif + namespace test_macros_detail { template struct is_same { enum { value = 0};} ;