diff --git a/stl/inc/xlocmon b/stl/inc/xlocmon index d8747b1e40..a8c1eafabd 100644 --- a/stl/inc/xlocmon +++ b/stl/inc/xlocmon @@ -364,7 +364,7 @@ protected: const char* _Eb = _Str.c_str(); char* _Ep; int _Errno = 0; - const long double _Ans = _Stodx_v2(_Eb, &_Ep, 0, &_Errno); // convert and "widen" double to long double + const long double _Ans = _Stodx_v3(_Eb, &_Ep, &_Errno); // convert and "widen" double to long double if (_Ep == _Eb || _Errno != 0) { _State |= ios_base::failbit; diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 97e47b146b..752f9a0d98 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -36,7 +36,7 @@ _END_EXTERN_C_UNLESS_PURE _STD_BEGIN -inline double _Stodx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) { // convert string to double +inline double _Stodx_v3(const char* _Str, char** _Endptr, int* _Perr) noexcept { // convert string to double int& _Errno_ref = errno; // Nonzero cost, pay it once const int _Orig = _Errno_ref; @@ -45,14 +45,10 @@ inline double _Stodx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) *_Perr = _Errno_ref; _Errno_ref = _Orig; - if (_Pten != 0) { - _Val *= _CSTD pow(10.0, static_cast(_Pten)); - } - return _Val; } -inline float _Stofx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) { // convert string to float +inline float _Stofx_v3(const char* _Str, char** _Endptr, int* _Perr) noexcept { // convert string to float int& _Errno_ref = errno; // Nonzero cost, pay it once const int _Orig = _Errno_ref; @@ -61,10 +57,6 @@ inline float _Stofx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) *_Perr = _Errno_ref; _Errno_ref = _Orig; - if (_Pten != 0) { - _Val *= _CSTD powf(10.0f, static_cast(_Pten)); - } - return _Val; } @@ -251,6 +243,11 @@ __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id; #endif // __clang__ #endif // !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) +struct _Num_get_parse_result { + int8_t _Base; // 0 for parsing failure, otherwise 10 or 16 + bool _Bad_grouping; +}; + _EXPORT_STD extern "C++" template >> class num_get : public locale::facet { // facet for converting text to encoded numbers public: @@ -366,7 +363,7 @@ protected: } else { // get long value char _Ac[_MAX_INT_DIG]; const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc()); // gather field - if (_Ac[0] == '\0') { // Handle N4727 [facet.num.get.virtuals]/3.6: + if (_Ac[0] == '\0') { // Handle N4944 [facet.num.get.virtuals]/3.9: // "zero, if the conversion function does not convert the entire field." // We should still do numeric conversion with bad digit separators, instead of // setting 0, but we can't distinguish that from _Getifld's interface, and _Getifld's @@ -540,10 +537,6 @@ protected: return _First; } -// TRANSITION, ABI: Sentinel value used by num_get::do_get() -// to enable correct "V2" behavior in _Getffld() and _Getffldx() -#define _ENABLE_V2_BEHAVIOR 1000000000 - // Size of char buffer used by num_get::do_get() for float/double/long double #define _FLOATING_BUFFER_SIZE (_MAX_EXP_DIG + _MAX_SIG_DIG_V2 + 16) @@ -551,20 +544,18 @@ protected: float& _Val) const { // get float from [_First, _Last) into _Val _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; - int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _Getffld(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field - if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 + const auto _Parse_result = + _Parse_fp_with_locale(_Ac, _MAX_SIG_DIG_V2, _First, _Last, _Iosbase.getloc()); // gather field + if (_Parse_result._Base == 0) { // ditto "fails to convert the entire field" _State = ios_base::failbit; _Val = 0.0f; } else { int _Errno; char* _Ep; - _Val = _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0) { + _Val = _STD _Stofx_v3(_Ac, &_Ep, &_Errno); // convert + if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtuals]/3 + || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtuals]/4 _State = ios_base::failbit; - _Val = 0.0f; - } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { - _Val = _CSTD ldexpf(_Val, 4 * _Hexexp); } } @@ -579,20 +570,18 @@ protected: double& _Val) const { // get double from [_First, _Last) into _Val _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; - int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _Getffld(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field - if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 + const auto _Parse_result = + _Parse_fp_with_locale(_Ac, _MAX_SIG_DIG_V2, _First, _Last, _Iosbase.getloc()); // gather field + if (_Parse_result._Base == 0) { // ditto "fails to convert the entire field" _State = ios_base::failbit; _Val = 0.0; } else { int _Errno; char* _Ep; - _Val = _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0) { + _Val = _STD _Stodx_v3(_Ac, &_Ep, &_Errno); // convert + if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtuals]/3 + || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtuals]/4 _State = ios_base::failbit; - _Val = 0.0; - } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { - _Val = _CSTD ldexp(_Val, 4 * _Hexexp); } } @@ -698,11 +687,11 @@ private: } const auto _Dlen = static_cast(_Base == 0 || _Base == 10 ? 10 : _Base == 8 ? 8 : 16 + 6); - string _Groups(1, static_cast(_Seendigit)); - size_t _Group = 0; + string _Groups(1, static_cast(_Seendigit)); // Groups are detected in the reversed order of _Groups. + size_t _Groups_arr_idx = 0; for (char* const _Pe = &_Ac[_MAX_INT_DIG - 1]; _First != _Last; ++_First) { // look for digits and separators - size_t _Idx = _Find_elem(_Atoms, *_First); + size_t _Idx = _STD _Find_elem(_Atoms, *_First); if (_Idx < _Dlen) { // got a digit, characterize it and add to group size *_Ptr = _Src[_Idx]; if ((_Nonzero || *_Ptr != '0') && _Ptr < _Pe) { @@ -711,33 +700,40 @@ private: } _Seendigit = true; - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; + if (_Groups[_Groups_arr_idx] != CHAR_MAX) { + ++_Groups[_Groups_arr_idx]; } - } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { + } else if (_Groups[_Groups_arr_idx] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { break; // not a group separator, done } else { // add a new group to _Groups string _Groups.push_back('\0'); - ++_Group; + ++_Groups_arr_idx; } } - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count + if (_Groups_arr_idx != 0) { + if (_Groups[_Groups_arr_idx] > '\0') { + ++_Groups_arr_idx; // add trailing group to group count } else { _Seendigit = false; // trailing separator, fail } } - for (const char* _Pg = &_Grouping[0]; _Seendigit && 0 < _Group;) { - if (*_Pg == CHAR_MAX) { - break; // end of grouping constraints to check - } else if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) { + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); + for (char _Current_grouping_count = '\0'; _Seendigit && _Groups_arr_idx > 0;) { + if (_Grouping_iter != _Grouping_end) { // keep the last value when _Grouping is exhausted + _Current_grouping_count = *_Grouping_iter; // if _Grouping is empty, '\0' is used + ++_Grouping_iter; + } + + --_Groups_arr_idx; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Groups_arr_idx > 0 && _Groups[_Groups_arr_idx] != _Current_grouping_count) + || (_Groups_arr_idx == 0 && _Groups[_Groups_arr_idx] > _Current_grouping_count))) { _Seendigit = false; // bad group size, fail - } else if ('\0' < _Pg[1]) { - ++_Pg; // group size okay, advance to next test } + // group size okay, advance to next test } if (_Seendigit && !_Nonzero) { @@ -750,171 +746,196 @@ private: return _Base; } - int __CLRCALL_OR_CDECL _Getffld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, - int* _Phexexp) const { // get floating-point field from [_First, _Last) into _Ac - if ((_Iosbase.flags() & ios_base::floatfield) == ios_base::hexfloat) { - return _Getffldx(_Ac, _First, _Last, _Iosbase, _Phexexp); // hex format - } - - const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); - const string _Grouping = _Punct_fac.grouping(); - char* _Ptr = _Ac; - bool _Bad = false; - bool _Sticky = false; + template // TRANSITION, ABI + static _Num_get_parse_result _Parse_fp_with_locale( + char* const _Ac, const int _Max_sig_dig, _InIt& _First, _InIt& _Last, const locale& _Loc) { + // get floating-point field from [_First, _Last) into _Ac + char* _Ptr = _Ac; - constexpr int _Numget_signoff = 10; - constexpr int _Numget_eoff = 12; - static constexpr char _Src[] = "0123456789-+Ee"; + constexpr size_t _Offset_dec_digit_end = 10; + constexpr size_t _Offset_hex_digit_end = 22; + constexpr size_t _Offset_neg_sign = 22; + constexpr size_t _Offset_pos_sign = 23; + constexpr size_t _Offset_upper_x = 24; + constexpr size_t _Offset_lower_x = 25; + constexpr size_t _Offset_upper_p = 26; + constexpr size_t _Offset_lower_p = 27; + constexpr size_t _Offset_upper_e = 14; + constexpr size_t _Offset_lower_e = 20; + static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; _Elem _Atoms[sizeof(_Src)]; - const ctype<_Elem>& _Ctype_fac = _STD use_facet>(_Iosbase.getloc()); + const auto& _Ctype_fac = _STD use_facet>(_Loc); _Ctype_fac.widen(_STD begin(_Src), _STD end(_Src), _Atoms); + const _Elem _Positive_sign = _Atoms[_Offset_pos_sign]; + const _Elem _Negative_sign = _Atoms[_Offset_neg_sign]; + const _Elem _Zero_wc = _Atoms[0]; + if (_First != _Last) { - if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign + if (*_First == _Positive_sign) { // gather plus sign *_Ptr++ = '+'; ++_First; - } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign + } else if (*_First == _Negative_sign) { // gather minus sign *_Ptr++ = '-'; ++_First; } } - char* _Leading = _Ptr; // remember backstop - *_Ptr++ = '0'; // backstop carries from sticky bit + *_Ptr++ = '0'; // backstop carries from sticky bit - bool _Seendigit = false; // seen a digit in input - int _Significant = 0; // number of significant digits - int _Pten = 0; // power of 10 multiplier - size_t _Idx; + bool _Parse_hex = false; + bool _Seendigit = false; // seen a digit in input + char _Initial_dec_leading_zero = '\0'; + if (_First != _Last && *_First == _Zero_wc) { + ++_First; + if (_First == _Last) { // "0" only + *_Ptr = '\0'; + return {10, false}; + } - const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); + if (*_First == _Atoms[_Offset_lower_x] || *_First == _Atoms[_Offset_upper_x]) { // 0x or 0X + _Parse_hex = true; + ++_First; // discard 0x or 0X for further parsing + *_Ptr++ = 'x'; + } else { + _Seendigit = true; + ++_Initial_dec_leading_zero; + } + } - const char* _Pg = &_Grouping[0]; - if (*_Pg == CHAR_MAX || *_Pg <= '\0') { - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Pten; - if (0 < _Idx) { - _Sticky = true; + bool _Has_unaccumulated_digits = false; + bool _Bad_grouping = false; + int _Significant = 0; // number of significant digits + ptrdiff_t _Power_of_rep_base = 0; // power of 10 or 16 + + const size_t _Offset_digit_end = _Parse_hex ? _Offset_hex_digit_end : _Offset_dec_digit_end; + const auto& _Punct_fac = _STD use_facet>(_Loc); + const string _Grouping = _Punct_fac.grouping(); + if (_Grouping.empty()) { + for (size_t _Idx; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant >= _Max_sig_dig) { + ++_Power_of_rep_base; // just scale by 10 or 16 + if (_Idx > 0) { + _Has_unaccumulated_digits = true; } } else if (_Idx != 0 || _Significant != 0) { // save a significant digit *_Ptr++ = _Src[_Idx]; ++_Significant; } } - } else { // grouping specified, gather digits and group sizes - const _Elem _Kseparator = _Grouping.empty() ? _Elem{} : _Punct_fac.thousands_sep(); - string _Groups(1, '\0'); - size_t _Group = 0; + } else { + const _Elem _Kseparator = _Punct_fac.thousands_sep(); + string _Groups(1, _Initial_dec_leading_zero); // Groups are detected in the reversed order of _Groups. + size_t _Groups_arr_idx = 0; for (; _First != _Last; ++_First) { - if ((_Idx = _Find_elem(_Atoms, *_First)) < 10) { // got a digit, add to group size + const size_t _Idx = _STD _Find_elem(_Atoms, *_First); + if (_Idx < _Offset_digit_end) { // got a digit, add to group size _Seendigit = true; - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Pten; - if (0 < _Idx) { - _Sticky = true; + if (_Significant >= _Max_sig_dig) { + ++_Power_of_rep_base; // just scale by 10 or 16 + if (_Idx > 0) { + _Has_unaccumulated_digits = true; } } else if (_Idx != 0 || _Significant != 0) { // save a significant digit *_Ptr++ = _Src[_Idx]; ++_Significant; } - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; + if (_Groups[_Groups_arr_idx] != CHAR_MAX) { + ++_Groups[_Groups_arr_idx]; } - } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { + } else if (_Groups[_Groups_arr_idx] == '\0' || *_First != _Kseparator) { break; // not a group separator, done } else { // add a new group to _Groups string _Groups.push_back('\0'); - ++_Group; + ++_Groups_arr_idx; } } - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count + if (_Groups_arr_idx != 0) { + if (_Groups[_Groups_arr_idx] > '\0') { + ++_Groups_arr_idx; // add trailing group to group count } else { - _Bad = true; // trailing separator, fail + _Bad_grouping = true; // trailing separator, fail } } - while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { - break; // end of grouping constraints to check + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping_iter + _Grouping.size(); + char _Current_grouping_count = '\0'; + while (!_Bad_grouping && _Groups_arr_idx > 0) { + if (_Grouping_iter != _Grouping_end) { // keep the last value when _Grouping is exhausted + _Current_grouping_count = *_Grouping_iter; // assign the variable at least once + ++_Grouping_iter; } - if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) { - _Bad = true; // bad group size, fail - } else if ('\0' < _Pg[1]) { - ++_Pg; // group size okay, advance to next test + --_Groups_arr_idx; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Groups_arr_idx > 0 && _Groups[_Groups_arr_idx] != _Current_grouping_count) + || (_Groups_arr_idx == 0 && _Groups[_Groups_arr_idx] > _Current_grouping_count))) { + _Bad_grouping = true; // bad group size, fail } + // group size okay, advance to next test } } + if (_Parse_hex && _Seendigit && _Significant == 0) { + // the condition is true when all of the digits after the 'x' and before the decimal point are zero + *_Ptr++ = '0'; // save at least one leading digit for hex + } + + const char _Decimal_point = _CSTD localeconv()->decimal_point[0]; if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . - *_Ptr++ = localeconv()->decimal_point[0]; + *_Ptr++ = _Decimal_point; ++_First; } - if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Atoms[0]; _Seendigit = true, (void) ++_First) { - --_Pten; // just count leading fraction zeros - } - - if (_Pten < 0) { // put one back - *_Ptr++ = '0'; - ++_Pten; + if (_Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_wc; _Seendigit = true, (void) ++_First) { + --_Power_of_rep_base; // count leading fraction zeros without storing digits into buffer } } - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) { + for (size_t _Idx; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + _Seendigit = true, (void) ++_First) { if (_Significant < _Max_sig_dig) { // save a significant fraction digit *_Ptr++ = _Src[_Idx]; ++_Significant; - } else if (0 < _Idx) { - _Sticky = true; // just update _Sticky + } else if (_Idx > 0) { + _Has_unaccumulated_digits = true; // just update _Has_unaccumulated_digits } } - if (_Sticky) { // increment ls digit in memory of those lost - char* _Px = _Ptr; - while (--_Px != _Leading) { // add in carry - if (*_Px != localeconv()->decimal_point[0]) { // not decimal point - if (*_Px != '9') { // carry stops here - ++*_Px; - break; - } - - *_Px = '0'; // propagate carry - } - } - - if (_Px == _Leading) { // change "999..." to "1000..." and scale _Pten - *_Px = '1'; - ++_Pten; + if (_Has_unaccumulated_digits) { // increment last digit in memory of those lost + char& _Last_got_digit = _Ptr[-1] == _Decimal_point ? _Ptr[-2] : _Ptr[-1]; + if (_Last_got_digit == '0' || _Last_got_digit == (_Parse_hex ? '8' : '5')) { + ++_Last_got_digit; } } + const _Elem _Lower_exp_wc = _Atoms[_Parse_hex ? _Offset_lower_p : _Offset_lower_e]; // 'e' for dec, 'p' for hex + const _Elem _Upper_exp_wc = _Atoms[_Parse_hex ? _Offset_upper_p : _Offset_upper_e]; // 'E' for dec, 'P' for hex + + bool _Exponent_part_negative = false; + ptrdiff_t _Exponent_part = 0; if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Numget_eoff + 1] - || *_First == _Atoms[_Numget_eoff])) { // 'e' or 'E', collect exponent - *_Ptr++ = 'e'; + && (*_First == _Lower_exp_wc || *_First == _Upper_exp_wc)) { // collect exponent ++_First; _Seendigit = false; _Significant = 0; if (_First != _Last) { - if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign - *_Ptr++ = '+'; + if (*_First == _Positive_sign) { // gather plus sign ++_First; - } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign - *_Ptr++ = '-'; + } else if (*_First == _Negative_sign) { // gather minus sign + _Exponent_part_negative = true; ++_First; } } - for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros + for (; _First != _Last && *_First == _Zero_wc; ++_First) { // strip leading zeros _Seendigit = true; } @@ -922,194 +943,128 @@ private: *_Ptr++ = '0'; // put one back } - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; + for (size_t _Idx; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Exponent_part < PTRDIFF_MAX / 10 + || (_Exponent_part == PTRDIFF_MAX / 10 + && static_cast(_Idx) <= PTRDIFF_MAX % 10)) { // save a significant exponent digit + _Exponent_part = _Exponent_part * 10 + static_cast(_Idx); + } else { + _Exponent_part = PTRDIFF_MAX; // saturated } } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } - - *_Ptr = '\0'; - return _Pten; - } - - int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, - int* _Phexexp) const { // get hex floating-point field from [_First, _Last) into _Ac - const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); - const string _Grouping = _Punct_fac.grouping(); - - constexpr int _Numget_signoff = 22; - constexpr int _Numget_xoff = 24; - constexpr int _Numget_poff = 26; - static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; - _Elem _Atoms[sizeof(_Src)]; - const ctype<_Elem>& _Ctype_fac = _STD use_facet>(_Iosbase.getloc()); - _Ctype_fac.widen(_STD begin(_Src), _STD end(_Src), _Atoms); - char* _Ptr = _Ac; - bool _Bad = false; - size_t _Idx; - - if (_First != _Last) { - if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign - *_Ptr++ = '-'; - ++_First; + if (_Exponent_part_negative) { + _Exponent_part = -_Exponent_part; } } - *_Ptr++ = '0'; - *_Ptr++ = 'x'; - - bool _Seendigit = false; // seen a digit in input - int _Significant = 0; // number of significant digits - int _Phex = 0; // power of 10 multiplier - - if (_First != _Last && *_First == _Atoms[0]) { - if (++_First != _Last && (*_First == _Atoms[_Numget_xoff + 1] || *_First == _Atoms[_Numget_xoff])) { - ++_First; // discard any 0x or 0X - } else { - _Seendigit = true; // '0' not followed by 'x' or 'X' - } + if (!_Seendigit) { + return {0, false}; } - const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - - const char* _Pg = &_Grouping[0]; - if (*_Pg == CHAR_MAX || *_Pg <= '\0') { - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff; - _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { - ++_Phex; // just scale by 10 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } else { // grouping specified, gather digits and group sizes - const _Elem _Kseparator = _Grouping.empty() ? _Elem{} : _Punct_fac.thousands_sep(); - string _Groups(1, '\0'); - size_t _Group = 0; + constexpr int _Dec_exp_abs_bound = 1100; // slightly greater than 324 + 768 + constexpr int _Hex_exp_abs_bound = 4200; // slightly greater than 1074 + 768 * 4 - for (; _First != _Last; ++_First) { - if ((_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff) { // got a digit, add to group size - _Seendigit = true; - if (_Max_sig_dig <= _Significant) { - ++_Phex; // just scale by 10 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } + const ptrdiff_t _Exp_abs_bound = _Parse_hex ? _Hex_exp_abs_bound : _Dec_exp_abs_bound; + const ptrdiff_t _Exp_rep_abs_bound = _Parse_hex ? _Hex_exp_abs_bound / 4 : _Dec_exp_abs_bound; - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; - } - } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { - break; // not a group separator, done - } else { // add a new group to _Groups string - _Groups.push_back('\0'); - ++_Group; + // basically _Exponent_part = _STD clamp(-_Exp_abs_bound, + // _Exponent_part + _Parse_hex ? _Power_of_rep_base * 4 : _Power_of_rep_base, _Exp_abs_bound) + // but need to defend overflowing + for (ptrdiff_t _Power_of_rep_adjusted = _Power_of_rep_base;;) { + if (_Exponent_part >= 0 && _Power_of_rep_adjusted >= 0 + && (_Exponent_part >= _Exp_abs_bound || _Power_of_rep_adjusted >= _Exp_rep_abs_bound)) { + _Exponent_part = _Exp_abs_bound; + break; + } else if (_Exponent_part <= 0 && _Power_of_rep_adjusted <= 0 + && (_Exponent_part <= -_Exp_abs_bound || _Power_of_rep_adjusted <= -_Exp_rep_abs_bound)) { + _Exponent_part = -_Exp_abs_bound; + break; + } else if (_STD abs(_Exponent_part) <= _Exp_abs_bound + && _STD abs(_Power_of_rep_adjusted) <= _Exp_rep_abs_bound) { + // _Exponent_part and _Power_of_rep_base are of different signedness, both of which are small enough + _Exponent_part += _Parse_hex ? _Power_of_rep_adjusted * 4 : _Power_of_rep_adjusted; + if (_Exponent_part > _Exp_abs_bound) { + _Exponent_part = _Exp_abs_bound; + } else if (_Exponent_part < -_Exp_abs_bound) { + _Exponent_part = -_Exp_abs_bound; } - } - - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count + break; + } else { + // only enters once: + // _Exponent_part and _Power_of_rep_base are of different signedness, but at least one is large + const ptrdiff_t _Exponent_part_preadjustment_round_up = + _Parse_hex ? (_STD abs(_Exponent_part) - 1) / 4 + 1 : _STD abs(_Exponent_part); + const ptrdiff_t _Exp_rep_adjustment = + (_STD min)(_Exponent_part_preadjustment_round_up, _STD abs(_Power_of_rep_base)); + + if (_Exponent_part >= 0) { + _Exponent_part -= _Parse_hex ? _Exp_rep_adjustment * 4 : _Exp_rep_adjustment; + _Power_of_rep_adjusted += _Exp_rep_adjustment; } else { - _Bad = true; // trailing separator, fail + _Exponent_part += _Parse_hex ? _Exp_rep_adjustment * 4 : _Exp_rep_adjustment; + _Power_of_rep_adjusted -= _Exp_rep_adjustment; } } - - while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { - break; // end of grouping constraints to check - } - - if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) { - _Bad = true; // bad group size, fail - } else if ('\0' < _Pg[1]) { - ++_Pg; // group size okay, advance to next test - } - } - } - - if (_Seendigit && _Significant == 0) { - *_Ptr++ = '0'; // save at least one leading digit - } - - if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . - *_Ptr++ = localeconv()->decimal_point[0]; - ++_First; } - if (_Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Atoms[0]; _Seendigit = true, (void) ++_First) { - --_Phex; // just count leading fraction zeros - } - - if (_Phex < 0) { // put one back - *_Ptr++ = '0'; - ++_Phex; + if (_Exponent_part != 0) { + *_Ptr++ = _Parse_hex ? 'p' : 'e'; + if (_Exponent_part < 0) { + *_Ptr++ = '-'; } - } - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff; - _Seendigit = true, (void) ++_First) { - if (_Significant < _Max_sig_dig) { // save a significant fraction digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; + char* const _Rev_begin = _Ptr; + for (ptrdiff_t _Exponent_part_abs = _STD abs(_Exponent_part); _Exponent_part_abs != 0; + _Exponent_part_abs /= 10) { + *_Ptr++ = static_cast('0' + _Exponent_part_abs % 10); } + _STD reverse(_Rev_begin, _Ptr); } - if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Numget_poff + 1] - || *_First == _Atoms[_Numget_poff])) { // 'p' or 'P', collect exponent - *_Ptr++ = 'p'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } + *_Ptr = '\0'; + return {static_cast(_Parse_hex ? 16 : 10), _Bad_grouping}; + } - for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros - _Seendigit = true; - } +// TRANSITION, ABI: Sentinel value used by num_get::do_get() +// to enable correct "V2" behavior in _Getffld() and _Getffldx() +#define _ENABLE_V2_BEHAVIOR 1000000000 - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } + int __CLRCALL_OR_CDECL _Getffld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) const { + // TRANSITION, ABI, unused now + // get floating-point field from [_First, _Last) into _Ac + static constexpr char _Src[] = "0123456789-+Ee"; // TRANSITION, ABI, was implicitly dllexported + const char* volatile _Ptr = &_Src[0]; + (void) _Ptr; + const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); + const auto _Parse_result = _Parse_fp_with_locale(_Ac, _Max_sig_dig, _First, _Last, _Iosbase.getloc()); + if (_Parse_result._Base == 0 || _Parse_result._Bad_grouping) { // TRANSITION, ABI, old behavior + *_Ac = '\0'; + } - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } + if (_Parse_result._Base == 16) { + *_Phexexp = 0; // power of 16 multiplier, unnecessary now } + return 0; // power of 10 multiplier, unnecessary now + } - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure + int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) const { + // TRANSITION, ABI, unused now + // get floating-point field from [_First, _Last) into _Ac + static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; // TRANSITION, ABI, was implicitly dllexported + const char* volatile _Ptr = &_Src[0]; + (void) _Ptr; + const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); + const auto _Parse_result = _Parse_fp_with_locale(_Ac, _Max_sig_dig, _First, _Last, _Iosbase.getloc()); + if (_Parse_result._Base == 0 || _Parse_result._Bad_grouping) { // TRANSITION, ABI, old behavior + *_Ac = '\0'; } - *_Ptr = '\0'; - *_Phexexp = _Phex; // power of 16 multiplier - return 0; // power of 10 multiplier + if (_Parse_result._Base == 16) { + *_Phexexp = 0; // power of 16 multiplier, unnecessary now + } + return 0; // power of 10 multiplier, unnecessary now } #undef _ENABLE_V2_BEHAVIOR @@ -1129,7 +1084,7 @@ __PURE_APPDOMAIN_GLOBAL locale::id num_get<_Elem, _InIt>::id; template int _Float_put_desired_precision(const streamsize _Precision, const ios_base::fmtflags _Float_flags) { - // return the effective precision determined by N4910 [facet.num.put.virtuals]/2.1 and printf's rules + // return the effective precision determined by N4944 [facet.num.put.virtuals]/2.1 and printf's rules const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); if (_Is_hex) { // return the number of hexits needed (after the radix point) to represent the floating-point value exactly diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 5066b3be08..c5e1ab3082 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -121,6 +121,10 @@ std/language.support/support.limits/support.limits.general/ranges.version.compil # libc++ is missing various DRs std/language.support/support.limits/support.limits.general/format.version.compile.pass.cpp FAIL +# libc++ doesn't implement LWG-2381 +std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_double.pass.cpp FAIL +std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_float.pass.cpp FAIL + # libc++ doesn't implement LWG-3670 std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass.cpp FAIL @@ -444,11 +448,6 @@ std/containers/sequences/array/array.swap/swap.fail.cpp FAIL std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp FAIL std/algorithms/alg.sorting/alg.min.max/minmax_init_list_comp.pass.cpp FAIL -# GH-1259 : wrong field extraction for hexfloats, or special cases like inf -std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_double.pass.cpp FAIL -std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_float.pass.cpp FAIL -std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long_double.pass.cpp FAIL - # GH-1277 : We don't match numpunct groups correctly in do_get std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp FAIL @@ -674,6 +673,9 @@ std/time/time.clock/time.clock.file/to_from_sys.pass.cpp FAIL # libc++'s filesystem::path::iterator models bidirectional_iterator, which is not guaranteed by the Standard std/input.output/filesystems/class.path/range_concept_conformance.compile.pass.cpp FAIL +# libc++ assumes long double is at least 80-bit; also affected by LWG-2381 +std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long_double.pass.cpp FAIL + # MaybePOCCAAllocator doesn't meet the allocator requirements std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp FAIL diff --git a/tests/std/test.lst b/tests/std/test.lst index 4915e01ab0..7016be4257 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -221,6 +221,7 @@ tests\GH_003105_piecewise_densities tests\GH_003119_error_category_ctor tests\GH_003246_cmath_narrowing tests\GH_003617_vectorized_meow_element +tests\LWG2381_num_get_floating_point tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\LWG3121_constrained_tuple_forwarding_ctor diff --git a/tests/std/tests/LWG2381_num_get_floating_point/env.lst b/tests/std/tests/LWG2381_num_get_floating_point/env.lst new file mode 100644 index 0000000000..19f025bd0e --- /dev/null +++ b/tests/std/tests/LWG2381_num_get_floating_point/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp new file mode 100644 index 0000000000..866cee0247 --- /dev/null +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -0,0 +1,720 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// derived from libc++'s test files: +// * std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_float.pass.cpp +// * std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_double.pass.cpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX17 +#include + +#include "../P0067R5_charconv/test.hpp" +// ^^^ needs to be included first ^^^ +#include "../P0067R5_charconv/double_from_chars_test_cases.hpp" +#include "../P0067R5_charconv/float_from_chars_test_cases.hpp" +#endif // _HAS_CXX17 + +using namespace std; + +class my_facet : public num_get { +public: + explicit my_facet(size_t refs = 0) : num_get(refs) {} +}; + +class mid_zero_numpunct : public numpunct { +public: + mid_zero_numpunct() : numpunct() {} + +protected: + string do_grouping() const override { + return "\1\0\2"s; + } +}; + +class my_numpunct : public numpunct { +public: + my_numpunct() : numpunct() {} + +protected: + char_type do_decimal_point() const override { + return ';'; + } + char_type do_thousands_sep() const override { + return '_'; + } + string do_grouping() const override { + return "\1\2\3"s; + } +}; + +template +void test() { + const my_facet f(1); + ios instr(nullptr); + Flt v = 0; + { + const char str[] = "123"; + assert((instr.flags() & instr.basefield) == instr.dec); + assert(instr.getloc().name() == "C"); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == 123); + } + { + const char str[] = "-123"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == -123); + } + { + const char str[] = "123.5"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == 123.5); + } + { + const char str[] = "125e-1"; + hex(instr); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == 125e-1); + } + { + const char str[] = "0x125p-1"; + hex(instr); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == static_cast(0x125) * 0.5); + } + { + v = -1; + const char str[] = "123_456_78_9;125"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + 3); + assert(err == instr.goodbit); + assert(v == 123); + } + { + v = -1; + const char str[] = "2-"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + 1); + assert(err == instr.goodbit); + assert(v == 2); + } + { + v = -1; + const char* inf_str = is_same_v ? "3.40283e+39" : "1.7979e+309"; // unrepresentable + const size_t len = strlen(inf_str); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(inf_str, inf_str + len + 1, instr, err, v); + assert(iter == inf_str + len); + assert(err == instr.failbit); + assert(v == HUGE_VAL); + } + { + v = -1; + const char* inf_str = is_same_v ? "-3.40283e+39" : "-1.7979e+309"; // unrepresentable + const size_t len = strlen(inf_str); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(inf_str, inf_str + len + 1, instr, err, v); + assert(iter == inf_str + len); + assert(err == instr.failbit); + assert(v == -HUGE_VAL); + } + + instr.imbue(locale(locale(), new my_numpunct)); + { + v = -1; + const char* sep_str = is_same_v ? "456_78_9;5" : "123_456_78_9;125"; + const size_t len = strlen(sep_str); + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == (is_same_v ? 456789.5 : 123456789.125)); + } + { + v = -1; + const char str[] = "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_" + "1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_1_2_3_4_5_6_7_8_9_0_"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.failbit); + } + { + v = -1; + const char str[] = "3;" + "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803" + "482534211706798214808651e+10"; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(str, str + sizeof(str), instr, err, v); + assert(iter == str + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(abs(v - 3.14159265358979e+10) / 3.14159265358979e+10 < 1.e-8); + } + { + v = -1; + const char str[] = "0x125p-1 "; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0x125p-1); + assert(str_instr.good()); + } + + // "0\n" and its friends + { + v = -1; + const char str[] = "0 "; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } + { + v = -1; + const char str[] = "+0 "; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } + { + v = -1; + const char str[] = "-0 "; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } + { + v = -1; + const char str[] = "0\n"; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } + { + v = -1; + const char str[] = "+0\n"; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } + { + v = -1; + const char str[] = "-0\n"; + istringstream str_instr(str); + str_instr >> v; + assert(v == 0); + assert(str_instr.good()); + } +} + +// Also test non-ending unlimited grouping for FP numbers +template , int> = 0> +void test_nonending_unlimited_grouping() { + const my_facet f(1); + ios instr(nullptr); + instr.imbue(locale(locale(), new mid_zero_numpunct)); + Flt v = 0; + { + v = -1; + const char sep_str[] = "17,2,9.0"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == 1729.0); + } + { + v = -1; + const char sep_str[] = "17,222,9.0"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == 172229.0); + } + { + v = -1; + const char sep_str[] = "0x17,2,9.0"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == 0x1729.0p0); + } + { + v = -1; + const char sep_str[] = "0x17,222,9.0"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == 0x172229.0p0); + } +} + +// Also test non-ending unlimited grouping for integers +template , int> = 0> +void test_nonending_unlimited_grouping() { + const my_facet f(1); + ios instr(nullptr); + instr.imbue(locale(locale(), new mid_zero_numpunct)); + + Integer v = 0; + { + v = static_cast(-1); + const char sep_str[] = "17,2,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == static_cast(1729)); + } + { + v = static_cast(-1); + const char sep_str[] = "17,222,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == static_cast(172229)); + } + { + v = static_cast(-1); + const char sep_str[] = "0x17,2,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + + instr.flags(ios_base::fmtflags{}); + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == static_cast(0x1729)); + } + { + v = static_cast(-1); + const char sep_str[] = "0x17,222,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + + instr.flags(ios_base::fmtflags{}); + const char* iter = f.get(sep_str, sep_str + len + 1, instr, err, v); + assert(iter == sep_str + len); + assert(err == instr.goodbit); + assert(v == static_cast(0x172229)); + } +} + +// Also test GH-1582 : "multiply by power of 10" logic is imprecise +template +void test_gh_1582() { + constexpr size_t digit_counts[]{200, 400, 800, 1600, 3200, 6400, 12800}; + for (const size_t n : digit_counts) { + { + istringstream iss{"1" + string(n, '0') + "e-" + to_string(n)}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"0." + string(n, '0') + "1e+" + to_string(n + 1)}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"0." + string(n, '0') + "1e" + to_string(n + 1)}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"2" + string(n, '0') + "e-" + to_string(n)}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0." + string(n, '0') + "2e+" + to_string(n + 1)}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0." + string(n, '0') + "2e" + to_string(n + 1)}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0x1" + string(n, '0') + "p-" + to_string(n * 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0x0." + string(n, '0') + "1p+" + to_string(n * 4 + 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0x0." + string(n, '0') + "1p" + to_string(n * 4 + 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0xA" + string(n, '0') + "p-" + to_string(n * 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0xA.0p0); + } + { + istringstream iss{"0x0." + string(n, '0') + "Ap+" + to_string(n * 4 + 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0xA.0p0); + } + { + istringstream iss{"0x0." + string(n, '0') + "Ap" + to_string(n * 4 + 4)}; + Flt x{}; + assert(iss >> x); + assert(x == 0xA.0p0); + } + } +} + +// Also test GH-3375 : Incorrect rounding when parsing long hexadecimal floating point numbers just above +// midpoints +template +void test_gh_3375() { + // Ensure long hexadecimal FP representations just above midpoints are correctly parsed. + if (is_same_v) { + { + istringstream stream("0x1.000001" + string(800, '0') + "1p+0"); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << hexfloat << x).str() == "0x1.0000020000000p+0"); + } + { + istringstream stream("-0x1.000001" + string(800, '0') + "1p+0"); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << hexfloat << x).str() == "-0x1.0000020000000p+0"); + } + } else { + { + istringstream stream("0x1.00000000000008" + string(800, '0') + "1p+0"); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << hexfloat << x).str() == "0x1.0000000000001p+0"); + } + { + istringstream stream("-0x1.00000000000008" + string(800, '0') + "1p+0"); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << hexfloat << x).str() == "-0x1.0000000000001p+0"); + } + } +} + +// Also test GH-3376 : Incorrect result when parsing 9.999999... +template +void test_gh_3376() { + + // Ensure that "0.0999....999" is still correctly parsed. + { + istringstream stream("0.09" + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "0.1"); + } + { + istringstream stream("-0.09" + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-0.1"); + } + + // Ensure that "8.999....999" is still correctly parsed. + { + istringstream stream("8." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "9"); + } + { + istringstream stream("-8." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-9"); + } + + // Ensure that "0.999...999" and its friends are correctly parsed. + { + istringstream stream("0." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "1"); + } + { + istringstream stream("-0." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-1"); + } + { + istringstream stream("9." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "10"); + } + { + istringstream stream("-9." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-10"); + } + + // Ensure that huge "999...999.999...999" representations are correctly parsed. + { + istringstream stream(string(38, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "1e+38"); + } + { + istringstream stream("-" + string(38, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-1e+38"); + } + { + istringstream stream(string(308, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + if (is_same_v) { + assert(!stream); + assert(x == HUGE_VALF); + } else { + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "1e+308"); + } + } + { + istringstream stream("-" + string(308, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + if (is_same_v) { + assert(!stream); + assert(x == -HUGE_VALF); + } else { + assert(static_cast(stream)); + assert((ostringstream{} << x).str() == "-1e+308"); + } + } +} + +// Also test GH-3378: : Incorrect rounding when parsing long floating point numbers just below midpoints +template +void test_gh_3378() { + { + // just below 2^-1022 + 2^-1074 + 2^-1075 + istringstream stream( + "2." + "2250738585072021241887014792022203290724052827943903781430313383743510731924419468675440643256388185138218" + "8218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926" + "7286329336304746701233168529834221527445172608358596545663192828352447877877998943107797838336991592885945" + "5521371418112845825114558431922307989750439508685941245723089173894616936837232119137365897797772328669884" + "0356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006" + "3173347090030127901881752034471902500280612777779167983910905785840064647159438105114891542827750411746821" + "9413395246668250343130618158782937900420539237507208336669324158000275839111885418864151316847843631308023" + "75962957739830017089843749e-308"); + + ostringstream os; + os.precision(17); + + Flt x = 0.0; + stream >> x; + assert(static_cast(stream)); + assert((move(os) << x).str() == "2.2250738585072019e-308"); + } + { + // negative case + istringstream stream( + "-2." + "2250738585072021241887014792022203290724052827943903781430313383743510731924419468675440643256388185138218" + "8218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926" + "7286329336304746701233168529834221527445172608358596545663192828352447877877998943107797838336991592885945" + "5521371418112845825114558431922307989750439508685941245723089173894616936837232119137365897797772328669884" + "0356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006" + "3173347090030127901881752034471902500280612777779167983910905785840064647159438105114891542827750411746821" + "9413395246668250343130618158782937900420539237507208336669324158000275839111885418864151316847843631308023" + "75962957739830017089843749e-308"); + + ostringstream os; + os.precision(17); + + Flt x = 0.0; + stream >> x; + assert(static_cast(stream)); + assert((move(os) << x).str() == "-2.2250738585072019e-308"); + } +} + +#if _HAS_CXX17 +void test_float_from_char_cases() { + for (const auto& test_case : float_from_chars_test_cases) { + auto repstr = test_case.fmt == chars_format::hex ? "0x" + string(test_case.input) : string(test_case.input); + istringstream is(repstr); + float x = 0.0f; + + const bool expected_no_err = test_case.correct_ec == errc{}; + const bool tested_no_err = static_cast(is >> x); + assert(expected_no_err == tested_no_err); + // TRANSITION, DevCom-10293606 + if (x != test_case.correct_value) { + char* endptr = nullptr; + assert(x == strtof(repstr.c_str(), &endptr)); + } + } +} + +template +void test_double_from_char_cases() { + for (const auto& test_case : double_from_chars_test_cases) { + auto repstr = test_case.fmt == chars_format::hex ? "0x" + string(test_case.input) : string(test_case.input); + istringstream is(repstr); + Flt x = 0.0; + + const bool expected_no_err = test_case.correct_ec == errc{}; + const bool tested_no_err = static_cast(is >> x); + assert(expected_no_err == tested_no_err); + // TRANSITION, DevCom-10293606 + if (x != test_case.correct_value) { + char* endptr = nullptr; + assert(x == strtod(repstr.c_str(), &endptr)); + } + } +} +#endif // _HAS_CXX17 + +int main() { + test(); + test(); + test(); + + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + test_nonending_unlimited_grouping(); + + test_gh_1582(); + test_gh_1582(); + test_gh_1582(); + + test_gh_3375(); + test_gh_3375(); + test_gh_3375(); + + test_gh_3376(); + test_gh_3376(); + test_gh_3376(); + + test_gh_3378(); + test_gh_3378(); + +#if _HAS_CXX17 + test_float_from_char_cases(); + test_double_from_char_cases(); + test_double_from_char_cases(); +#endif // _HAS_CXX17 +} diff --git a/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp b/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp index 2bacb2b91a..fb90e0cffd 100644 --- a/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp +++ b/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp @@ -19,7 +19,7 @@ using namespace std; template void test_case_unsigned(const string& maxValue, const string& maxValuePlusOne) { - // See N4727 [facet.num.get.virtuals]/3.6 + // See N4944 [facet.num.get.virtuals]/3.9 // * "zero, if the conversion function does not convert the entire field" { istringstream src("-"s); @@ -89,7 +89,7 @@ void test_case_unsigned(const string& maxValue, const string& maxValuePlusOne) { template void test_case_signed( const string& minValueMinusOne, const string& minValue, const string& maxValue, const string& maxValuePlusOne) { - // See N4727 [facet.num.get.virtuals]/3.6 + // See N4944 [facet.num.get.virtuals]/3.9 // * "zero, if the conversion function does not convert the entire field" { istringstream src("-"s); @@ -176,12 +176,12 @@ void test_case_signed( template void test_case_float(const string& outOfRangeValue) { - // reals don't get any of the int special cases and always set 0 on failure + // reals don't get any of the int special cases and always have the converted value { istringstream src(outOfRangeValue); Float result = 17.49f; src >> result; - assert(result == static_cast(0)); + assert(result == numeric_limits::infinity()); assert(src.rdstate() == (ios_base::failbit | ios_base::eofbit)); } @@ -189,7 +189,7 @@ void test_case_float(const string& outOfRangeValue) { istringstream src("-" + outOfRangeValue); Float result = 17.49f; src >> result; - assert(result == static_cast(0)); + assert(result == -numeric_limits::infinity()); assert(src.rdstate() == (ios_base::failbit | ios_base::eofbit)); }