From 5c5ff34c157053a3843ca058d9a8e467e3ff6cb2 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 17:49:20 +0800 Subject: [PATCH 01/57] Implement LWG-2381 --- stl/inc/xlocnum | 527 ++++++++++++++++++++++++------------------------ 1 file changed, 259 insertions(+), 268 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 97e47b146b..0734296a9c 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -559,10 +559,12 @@ protected: } else { int _Errno; char* _Ep; - _Val = _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert + _Val = _STD _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; - _Val = 0.0f; + if (_Errno != ERANGE || (_Val != HUGE_VALF && _Val != -HUGE_VALF)) { + _Val = 0.0f; + } } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { _Val = _CSTD ldexpf(_Val, 4 * _Hexexp); } @@ -587,10 +589,12 @@ protected: } else { int _Errno; char* _Ep; - _Val = _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert + _Val = _STD _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; - _Val = 0.0; + if (_Errno != ERANGE || (_Val != HUGE_VAL && _Val != -HUGE_VAL)) { + _Val = 0.0f; + } } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { _Val = _CSTD ldexp(_Val, 4 * _Hexexp); } @@ -750,366 +754,353 @@ 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 - } - + 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 const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); const string _Grouping = _Punct_fac.grouping(); char* _Ptr = _Ac; bool _Bad = false; bool _Sticky = false; - constexpr int _Numget_signoff = 10; - constexpr int _Numget_eoff = 12; - static constexpr char _Src[] = "0123456789-+Ee"; + constexpr int _Dec_digits_cnt = 10; + constexpr int _Hex_signoff = 22; + constexpr int _Numget_xoff = 24; + constexpr int _Numget_poff = 26; + constexpr int _Numget_upper_e_off = 0xE; + constexpr int _Numget_lower_e_off = 0xE + 0x10 - 10; + 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); + const _Elem _Positive_sign = _Atoms[_Hex_signoff + 1]; + const _Elem _Negative_sign = _Atoms[_Hex_signoff]; + const _Elem _Upper_e = _Atoms[_Numget_upper_e_off]; + const _Elem _Lower_e = _Atoms[_Numget_lower_e_off]; + const _Elem _Zero_ch = _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 + char* const _Leading = _Ptr; // remember backstop + *_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; 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)) < 10; _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Pten; - if (0 < _Idx) { - _Sticky = true; - } - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } + bool _Parse_hex = false; + if (_First != _Last && *_First == _Zero_ch) { + ++_First; + if (_First != _Last + && (*_First == _Atoms[_Numget_xoff] || *_First == _Atoms[_Numget_xoff + 1])) { // 0X or 0x + _Parse_hex = true; + ++_First; // discard 0X or 0x for further parsing } - } 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; + } - for (; _First != _Last; ++_First) { - if ((_Idx = _Find_elem(_Atoms, *_First)) < 10) { // 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; - } + const char* _Pg = &_Grouping[0]; + + if (_Parse_hex) { + int _Phex = 0; // power of 10 multiplier + + *_Ptr++ = 'x'; + + if (*_Pg == CHAR_MAX || *_Pg <= '\0') { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_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; + + for (; _First != _Last; ++_First) { + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_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; + } - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; + 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; } - } 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; } - } - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail + if (_Group != 0) { + if ('\0' < _Groups[_Group]) { + ++_Group; // add trailing group to group count + } else { + _Bad = true; // trailing separator, fail + } } - } - while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { - break; // end of grouping constraints to check - } + 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 ((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 (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . - *_Ptr++ = localeconv()->decimal_point[0]; - ++_First; - } + if (_Seendigit && _Significant == 0) { + *_Ptr++ = '0'; // save at least one leading digit + } - 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 (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . + *_Ptr++ = _CSTD localeconv()->decimal_point[0]; + ++_First; } - if (_Pten < 0) { // put one back - *_Ptr++ = '0'; - ++_Pten; + if (_Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { + --_Phex; // just count leading fraction zeros + } + + if (_Phex < 0) { // put one back + *_Ptr++ = '0'; + ++_Phex; + } } - } - for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _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 + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff; + _Seendigit = true, (void) ++_First) { + if (_Significant < _Max_sig_dig) { // save a significant fraction digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } } - } - 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; + 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 == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; } + } - *_Px = '0'; // propagate carry + for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros + _Seendigit = true; } - } - if (_Px == _Leading) { // change "999..." to "1000..." and scale _Pten - *_Px = '1'; - ++_Pten; - } - } + if (_Seendigit) { + *_Ptr++ = '0'; // put one back + } - if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Numget_eoff + 1] - || *_First == _Atoms[_Numget_eoff])) { // 'e' or 'E', collect exponent - *_Ptr++ = 'e'; - ++_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; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff; + _Seendigit = true, (void) ++_First) { + if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } } } - for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros - _Seendigit = true; + if (_Bad || !_Seendigit) { + _Ptr = _Ac; // roll back pointer to indicate failure } - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } + *_Ptr = '\0'; + *_Phexexp = _Phex; // power of 16 multiplier + return 0; // power of 10 multiplier + } else { + int _Pten = 0; // power of 10 multiplier - 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; + if (*_Pg == CHAR_MAX || *_Pg <= '\0') { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; + _Seendigit = true, (void) ++_First) { + if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky + ++_Pten; + if (0 < _Idx) { + _Sticky = true; + } + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } } - } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } + } 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; + + for (; _First != _Last; ++_First) { + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt) { // 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; + } + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } - *_Ptr = '\0'; - return _Pten; - } + 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; + } + } - 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(); + if (_Group != 0) { + if ('\0' < _Groups[_Group]) { + ++_Group; // add trailing group to group count + } else { + _Bad = true; // trailing separator, fail + } + } - 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); + while (!_Bad && 0 < _Group) { + if (*_Pg == CHAR_MAX) { + break; // end of grouping constraints to check + } - char* _Ptr = _Ac; - bool _Bad = false; - size_t _Idx; + 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 (_First != _Last) { - if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign - *_Ptr++ = '-'; + const char _Decimal_point = localeconv()->decimal_point[0]; + if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . + *_Ptr++ = _Decimal_point; ++_First; } - } - *_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 (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { + --_Pten; // just count leading fraction zeros + } - 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 (_Pten < 0) { // put one back + *_Ptr++ = '0'; + ++_Pten; + } } - } - - 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; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { - ++_Phex; // just scale by 10 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + if (_Significant < _Max_sig_dig) { // save a significant fraction digit *_Ptr++ = _Src[_Idx]; ++_Significant; + } else if (0 < _Idx) { + _Sticky = true; // just update _Sticky } } - } 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; - 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; - } + if (_Sticky) { // increment ls digit in memory of those lost + char* _Px = _Ptr; + while (--_Px != _Leading) { // add in carry + if (*_Px != _Decimal_point) { // not decimal point + if (*_Px != '9') { // carry stops here + ++*_Px; + break; + } - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; + *_Px = '0'; // propagate carry } - } 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; } - } - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail + if (_Px == _Leading) { // change "999..." to "1000..." and scale _Pten + *_Px = '1'; + ++_Pten; } } - while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { - break; // end of grouping constraints to check + if (_Seendigit && _First != _Last + && (*_First == _Lower_e || *_First == _Upper_e)) { // 'e' or 'E', collect exponent + *_Ptr++ = 'e'; + ++_First; + _Seendigit = false; + _Significant = 0; + + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; + } } - 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 + for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros + _Seendigit = true; } - } - } - - 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; - } - } - - 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; - } - } - - 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; + if (_Seendigit) { + *_Ptr++ = '0'; // put one back } - } - for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros - _Seendigit = true; - } - - if (_Seendigit) { - *_Ptr++ = '0'; // put one back + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; + _Seendigit = true, (void) ++_First) { + if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + } } - 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 (_Bad || !_Seendigit) { + _Ptr = _Ac; // roll back pointer to indicate failure } - } - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure + *_Ptr = '\0'; + return _Pten; } + } - *_Ptr = '\0'; - *_Phexexp = _Phex; // power of 16 multiplier - return 0; // power of 10 multiplier + int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) const { + // TRANSITION, ABI, unused now + // get hex floating-point field from [_First, _Last) into _Ac + return _Getffld(_Ac, _First, _Last, _Iosbase, _Phexexp); } #undef _ENABLE_V2_BEHAVIOR From c6f272aab929ba4f5496321e6e1ffc5d9608d4b5 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 17:49:31 +0800 Subject: [PATCH 02/57] Test coverage for LWG-2381 --- tests/std/test.lst | 1 + .../LWG2318_num_get_floating_point/env.lst | 4 + .../LWG2318_num_get_floating_point/test.cpp | 206 ++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 tests/std/tests/LWG2318_num_get_floating_point/env.lst create mode 100644 tests/std/tests/LWG2318_num_get_floating_point/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 9652c56a55..a05efa405c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -218,6 +218,7 @@ tests\GH_003022_substr_allocator tests\GH_003105_piecewise_densities tests\GH_003119_error_category_ctor tests\GH_003246_cmath_narrowing +tests\LWG2318_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/LWG2318_num_get_floating_point/env.lst b/tests/std/tests/LWG2318_num_get_floating_point/env.lst new file mode 100644 index 0000000000..19f025bd0e --- /dev/null +++ b/tests/std/tests/LWG2318_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/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp new file mode 100644 index 0000000000..6dc8fffcf8 --- /dev/null +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class my_facet : public num_get { +public: + explicit my_facet(size_t refs = 0) : num_get(refs) {} +}; + +class my_numpunct : public numpunct { +public: + my_numpunct() : numpunct() {} + +protected: + virtual char_type do_decimal_point() const { + return ';'; + } + virtual char_type do_thousands_sep() const { + return '_'; + } + virtual string do_grouping() const { + return string("\1\2\3"); + } +}; + +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); + } + { + // See PR11871 + 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); + } + if constexpr (is_same_v) { + { + v = -1; + const char str[] = "3.40283e+39"; // unrepresentable + 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); + assert(v == HUGE_VALF); + } + { + v = -1; + const char str[] = "-3.40283e+38"; // unrepresentable + 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); + assert(v == -HUGE_VALF); + } + } else { + { + v = -1; + const char str[] = "1.79779e+309"; // unrepresentable + 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); + assert(v == HUGE_VAL); + } + { + v = -1; + const char str[] = "-1.79779e+308"; // unrepresentable + 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); + assert(v == -HUGE_VAL); + } + } + + instr.imbue(locale(locale(), new my_numpunct)); + if constexpr (is_same_v) { + v = -1; + const char str[] = "456_78_9;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 == 456789.5); + } else { + 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 + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == 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 "; + std::istringstream f(str); + f >> v; + assert(v == 0x125p-1); + assert(f.good()); + } +} + +int main() { + test(); + test(); + test(); +} From 7d572f13d87d96de655bcfb29da627cd4baaa2cc Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 17:53:12 +0800 Subject: [PATCH 03/57] Update references to WD to WG21-N4928 --- stl/inc/xlocnum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 0734296a9c..eccdb0afde 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -366,7 +366,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 N4928 [facet.num.get.virtuals]/3.3.6: // "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 @@ -1120,7 +1120,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 N4928 [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 From e28c00d50c3a0e2bd6834fef7db88d660741a72a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 18:06:03 +0800 Subject: [PATCH 04/57] Add comments for derivation and remove `std::` --- tests/std/tests/LWG2318_num_get_floating_point/test.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 6dc8fffcf8..573666ce63 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. // 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_long_double.pass.cpp + #include #include #include @@ -192,7 +196,7 @@ void test() { { v = -1; const char str[] = "0x125p-1 "; - std::istringstream f(str); + istringstream f(str); f >> v; assert(v == 0x125p-1); assert(f.good()); From 5b7322c51d7f456195de087db22eb31c750dfb35 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 18:17:22 +0800 Subject: [PATCH 05/57] Missing clang-format --- tests/std/tests/LWG2318_num_get_floating_point/test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 573666ce63..408abe610b 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -192,7 +192,6 @@ void test() { assert(abs(v - 3.14159265358979e+10) / 3.14159265358979e+10 < 1.e-8); } - { v = -1; const char str[] = "0x125p-1 "; From 2fce167f0d04231b765f1b616402d19c41114ef5 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 19:21:13 +0800 Subject: [PATCH 06/57] Handle some test errors --- stl/inc/xlocnum | 9 +- .../LWG2318_num_get_floating_point/test.cpp | 128 ++++++++++-------- .../VSO_0234888_num_get_overflows/test.cpp | 4 +- 3 files changed, 81 insertions(+), 60 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index eccdb0afde..5fbdff8b0f 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -798,13 +798,16 @@ private: const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - bool _Parse_hex = false; + bool _Parse_hex = false; + bool _Has_dec_leading_zero = false; if (_First != _Last && *_First == _Zero_ch) { ++_First; if (_First != _Last && (*_First == _Atoms[_Numget_xoff] || *_First == _Atoms[_Numget_xoff + 1])) { // 0X or 0x _Parse_hex = true; ++_First; // discard 0X or 0x for further parsing + } else { + _Has_dec_leading_zero = true; } } @@ -963,6 +966,10 @@ private: string _Groups(1, '\0'); size_t _Group = 0; + if (_Has_dec_leading_zero) { + ++_Groups[_Group]; + } + for (; _First != _Last; ++_First) { if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt) { // got a digit, add to group size _Seendigit = true; diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 408abe610b..8b345e5c48 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include using namespace std; @@ -36,6 +37,74 @@ class my_numpunct : public numpunct { } }; +template , int> = 0> +void test_inf(ios& instr, const my_facet& f) { + Flt v = 0.0; + { + v = -1; + const char str[] = "3.40283e+39"; // unrepresentable + 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); + assert(v == HUGE_VALF); + } + { + v = -1; + const char str[] = "-3.40283e+38"; // unrepresentable + 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); + assert(v == -HUGE_VALF); + } +} + +template , int> = 0> +void test_inf(ios& instr, const my_facet& f) { + Flt v = 0.0; + { + v = -1; + const char str[] = "1.79779e+309"; // unrepresentable + 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); + assert(v == HUGE_VAL); + } + { + v = -1; + const char str[] = "-1.79779e+308"; // unrepresentable + 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); + assert(v == -HUGE_VAL); + } +} + +template , int> = 0> +void test_sep(ios& instr, const my_facet& f) { + Flt v = -1; + const char str[] = "456_78_9;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 == 456789.5); +} + +template , int> = 0> +void test_sep(ios& instr, const my_facet& f) { + Flt 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 + sizeof(str) - 1); + assert(err == instr.goodbit); + assert(v == 123456789.125); +} + template void test() { const my_facet f(1); @@ -95,7 +164,6 @@ void test() { assert(v == 123); } { - // See PR11871 v = -1; const char str[] = "2-"; ios_base::iostate err = instr.goodbit; @@ -104,64 +172,10 @@ void test() { assert(err == instr.goodbit); assert(v == 2); } - if constexpr (is_same_v) { - { - v = -1; - const char str[] = "3.40283e+39"; // unrepresentable - 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); - assert(v == HUGE_VALF); - } - { - v = -1; - const char str[] = "-3.40283e+38"; // unrepresentable - 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); - assert(v == -HUGE_VALF); - } - } else { - { - v = -1; - const char str[] = "1.79779e+309"; // unrepresentable - 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); - assert(v == HUGE_VAL); - } - { - v = -1; - const char str[] = "-1.79779e+308"; // unrepresentable - 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); - assert(v == -HUGE_VAL); - } - } + test_inf(instr, f); instr.imbue(locale(locale(), new my_numpunct)); - if constexpr (is_same_v) { - v = -1; - const char str[] = "456_78_9;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 == 456789.5); - } else { - 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 + sizeof(str) - 1); - assert(err == instr.goodbit); - assert(v == 123456789.125); - } + test_sep(instr, f); { 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_" 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..329a3c7c90 100644 --- a/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp +++ b/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp @@ -181,7 +181,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)); } @@ -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)); } From 22260ca1392daef236756444baec026483bec8e1 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 20:01:03 +0800 Subject: [PATCH 07/57] Handle "0" and name shadowing --- stl/inc/xlocnum | 7 +++++-- tests/std/tests/LWG2318_num_get_floating_point/test.cpp | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 5fbdff8b0f..27cbb3b735 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -802,8 +802,11 @@ private: bool _Has_dec_leading_zero = false; if (_First != _Last && *_First == _Zero_ch) { ++_First; - if (_First != _Last - && (*_First == _Atoms[_Numget_xoff] || *_First == _Atoms[_Numget_xoff + 1])) { // 0X or 0x + if (_First == _Last) { // "0" only + *_Ptr = '\0'; + return 0; + } + if (*_First == _Atoms[_Numget_xoff] || *_First == _Atoms[_Numget_xoff + 1]) { // 0X or 0x _Parse_hex = true; ++_First; // discard 0X or 0x for further parsing } else { diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 8b345e5c48..934b5ec7df 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -209,10 +209,10 @@ void test() { { v = -1; const char str[] = "0x125p-1 "; - istringstream f(str); - f >> v; + istringstream str_instr(str); + str_instr >> v; assert(v == 0x125p-1); - assert(f.good()); + assert(str_instr.good()); } } From df459f98a13d77b45ae77429a16105046aa17311 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 22:45:15 +0800 Subject: [PATCH 08/57] Remove possibly superfluous zero-setting This seems redundant. --- stl/inc/xlocnum | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 27cbb3b735..97617737ec 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -562,9 +562,6 @@ protected: _Val = _STD _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; - if (_Errno != ERANGE || (_Val != HUGE_VALF && _Val != -HUGE_VALF)) { - _Val = 0.0f; - } } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { _Val = _CSTD ldexpf(_Val, 4 * _Hexexp); } @@ -592,9 +589,6 @@ protected: _Val = _STD _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; - if (_Errno != ERANGE || (_Val != HUGE_VAL && _Val != -HUGE_VAL)) { - _Val = 0.0f; - } } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { _Val = _CSTD ldexp(_Val, 4 * _Hexexp); } From 432e04521bf3c28a64395ee89aaf0ab35ca167b2 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 27 Jan 2023 22:46:10 +0800 Subject: [PATCH 09/57] Update references to WD and fix a comment --- tests/std/tests/VSO_0234888_num_get_overflows/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 329a3c7c90..74934ac3a8 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 N4928 [facet.num.get.virtuals]/3.3.6 // * "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 N4928 [facet.num.get.virtuals]/3.3.6 // * "zero, if the conversion function does not convert the entire field" { istringstream src("-"s); @@ -176,7 +176,7 @@ 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; From 49c6e6998ba9c89a60e85bb3d4410c15af38af51 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 28 Jan 2023 13:42:03 +0800 Subject: [PATCH 10/57] Address most of @strega-nil-ms's review comments Co-authored-by: nicole mazzuca <83086508+strega-nil-ms@users.noreply.github.com> --- stl/inc/xlocnum | 126 ++++++++++-------- .../LWG2318_num_get_floating_point/test.cpp | 104 +++++---------- 2 files changed, 100 insertions(+), 130 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 97617737ec..043efcc69c 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -552,7 +552,7 @@ protected: _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 + const int _Base = _Getffldx(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0f; @@ -579,7 +579,7 @@ protected: _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 + const int _Base = _Getffldx(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0; @@ -749,6 +749,12 @@ private: } 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 + return _Getffldx(_Ac, _First, _Last, _Iosbase, _Phexexp); + } + + int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) const { // get floating-point field from [_First, _Last) into _Ac const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); const string _Grouping = _Punct_fac.grouping(); @@ -756,21 +762,25 @@ private: bool _Bad = false; bool _Sticky = false; - constexpr int _Dec_digits_cnt = 10; - constexpr int _Hex_signoff = 22; - constexpr int _Numget_xoff = 24; - constexpr int _Numget_poff = 26; - constexpr int _Numget_upper_e_off = 0xE; - constexpr int _Numget_lower_e_off = 0xE + 0x10 - 10; - static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; + constexpr int _Offset_dec_digit_end = 10; + constexpr int _Offset_hex_digit_end = 22; + constexpr int _Offset_neg_sign = 22; + constexpr int _Offset_pos_sign = _Offset_neg_sign + 1; + constexpr int _Offset_upper_x = 24; + constexpr int _Offset_lower_x = _Offset_upper_x + 1; + constexpr int _Offset_upper_p = 26; + constexpr int _Offset_lower_p = _Offset_upper_p + 1; + constexpr int _Offset_upper_e = 0xE; + constexpr int _Offset_lower_e = 0xE + 0x10 - 10; + 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); - const _Elem _Positive_sign = _Atoms[_Hex_signoff + 1]; - const _Elem _Negative_sign = _Atoms[_Hex_signoff]; - const _Elem _Upper_e = _Atoms[_Numget_upper_e_off]; - const _Elem _Lower_e = _Atoms[_Numget_lower_e_off]; + const _Elem _Positive_sign = _Atoms[_Offset_pos_sign]; + const _Elem _Negative_sign = _Atoms[_Offset_neg_sign]; + const _Elem _Upper_e = _Atoms[_Offset_upper_e]; + const _Elem _Lower_e = _Atoms[_Offset_lower_e]; const _Elem _Zero_ch = _Atoms[0]; if (_First != _Last) { @@ -800,7 +810,7 @@ private: *_Ptr = '\0'; return 0; } - if (*_First == _Atoms[_Numget_xoff] || *_First == _Atoms[_Numget_xoff + 1]) { // 0X or 0x + 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 } else { @@ -808,18 +818,18 @@ private: } } - const char* _Pg = &_Grouping[0]; + const char* _Current_grouping = &_Grouping[0]; if (_Parse_hex) { - int _Phex = 0; // power of 10 multiplier + int _Power_of_16 = 0; *_Ptr++ = 'x'; - if (*_Pg == CHAR_MAX || *_Pg <= '\0') { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff; + if (*_Current_grouping == CHAR_MAX || *_Current_grouping <= '\0') { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; _Seendigit = true, (void) ++_First) { if (_Max_sig_dig <= _Significant) { - ++_Phex; // just scale by 10 + ++_Power_of_16; // just scale by 16 } else if (_Idx != 0 || _Significant != 0) { // save a significant digit *_Ptr++ = _Src[_Idx]; ++_Significant; @@ -831,10 +841,11 @@ private: size_t _Group = 0; for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff) { // got a digit, add to group size + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) + < _Offset_hex_digit_end) { // got a digit, add to group size _Seendigit = true; if (_Max_sig_dig <= _Significant) { - ++_Phex; // just scale by 10 + ++_Power_of_16; // just scale by 16 } else if (_Idx != 0 || _Significant != 0) { // save a significant digit *_Ptr++ = _Src[_Idx]; ++_Significant; @@ -860,14 +871,15 @@ private: } while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { + if (*_Current_grouping == CHAR_MAX) { break; // end of grouping constraints to check } - if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) { + if ((0 < --_Group && *_Current_grouping != _Groups[_Group]) + || (0 == _Group && *_Current_grouping < _Groups[_Group])) { _Bad = true; // bad group size, fail - } else if ('\0' < _Pg[1]) { - ++_Pg; // group size okay, advance to next test + } else if ('\0' < _Current_grouping[1]) { + ++_Current_grouping; // group size okay, advance to next test } } } @@ -883,16 +895,16 @@ private: if (_Significant == 0) { // 0000. so far for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Phex; // just count leading fraction zeros + --_Power_of_16; // just count leading fraction zeros } - if (_Phex < 0) { // put one back + if (_Power_of_16 < 0) { // put one back *_Ptr++ = '0'; - ++_Phex; + ++_Power_of_16; } } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; _Seendigit = true, (void) ++_First) { if (_Significant < _Max_sig_dig) { // save a significant fraction digit *_Ptr++ = _Src[_Idx]; @@ -901,8 +913,8 @@ private: } if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Numget_poff + 1] - || *_First == _Atoms[_Numget_poff])) { // 'p' or 'P', collect exponent + && (*_First == _Atoms[_Offset_lower_p] + || *_First == _Atoms[_Offset_upper_p])) { // 'p' or 'P', collect exponent *_Ptr++ = 'p'; ++_First; _Seendigit = false; @@ -926,7 +938,7 @@ private: *_Ptr++ = '0'; // put one back } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Hex_signoff; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; _Seendigit = true, (void) ++_First) { if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit *_Ptr++ = _Src[_Idx]; @@ -940,16 +952,16 @@ private: } *_Ptr = '\0'; - *_Phexexp = _Phex; // power of 16 multiplier - return 0; // power of 10 multiplier + *_Phexexp = _Power_of_16; // power of 16 multiplier + return 0; } else { - int _Pten = 0; // power of 10 multiplier + int _Power_of_10 = 0; - if (*_Pg == CHAR_MAX || *_Pg <= '\0') { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; + if (*_Current_grouping == CHAR_MAX || *_Current_grouping <= '\0') { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; _Seendigit = true, (void) ++_First) { if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Pten; + ++_Power_of_10; if (0 < _Idx) { _Sticky = true; } @@ -968,10 +980,11 @@ private: } for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt) { // got a digit, add to group size + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) + < _Offset_dec_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; + ++_Power_of_10; if (0 < _Idx) { _Sticky = true; } @@ -1000,19 +1013,20 @@ private: } while (!_Bad && 0 < _Group) { - if (*_Pg == CHAR_MAX) { + if (*_Current_grouping == CHAR_MAX) { break; // end of grouping constraints to check } - if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) { + if ((0 < --_Group && *_Current_grouping != _Groups[_Group]) + || (0 == _Group && *_Current_grouping < _Groups[_Group])) { _Bad = true; // bad group size, fail - } else if ('\0' < _Pg[1]) { - ++_Pg; // group size okay, advance to next test + } else if ('\0' < _Current_grouping[1]) { + ++_Current_grouping; // group size okay, advance to next test } } } - const char _Decimal_point = localeconv()->decimal_point[0]; + const char _Decimal_point = _CSTD localeconv()->decimal_point[0]; if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . *_Ptr++ = _Decimal_point; ++_First; @@ -1020,16 +1034,16 @@ private: if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Pten; // just count leading fraction zeros + --_Power_of_10; // just count leading fraction zeros } - if (_Pten < 0) { // put one back + if (_Power_of_10 < 0) { // put one back *_Ptr++ = '0'; - ++_Pten; + ++_Power_of_10; } } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; _Seendigit = true, (void) ++_First) { if (_Significant < _Max_sig_dig) { // save a significant fraction digit *_Ptr++ = _Src[_Idx]; @@ -1052,9 +1066,9 @@ private: } } - if (_Px == _Leading) { // change "999..." to "1000..." and scale _Pten + if (_Px == _Leading) { // change "999..." to "1000..." and scale _Power_of_10 *_Px = '1'; - ++_Pten; + ++_Power_of_10; } } @@ -1083,7 +1097,7 @@ private: *_Ptr++ = '0'; // put one back } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Dec_digits_cnt; + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; _Seendigit = true, (void) ++_First) { if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit *_Ptr++ = _Src[_Idx]; @@ -1097,16 +1111,10 @@ private: } *_Ptr = '\0'; - return _Pten; + return _Power_of_10; } } - int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) const { - // TRANSITION, ABI, unused now - // get hex floating-point field from [_First, _Last) into _Ac - return _Getffld(_Ac, _First, _Last, _Iosbase, _Phexexp); - } - #undef _ENABLE_V2_BEHAVIOR }; diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 934b5ec7df..ab72ff1905 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -3,11 +3,12 @@ // 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_long_double.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 @@ -37,74 +38,6 @@ class my_numpunct : public numpunct { } }; -template , int> = 0> -void test_inf(ios& instr, const my_facet& f) { - Flt v = 0.0; - { - v = -1; - const char str[] = "3.40283e+39"; // unrepresentable - 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); - assert(v == HUGE_VALF); - } - { - v = -1; - const char str[] = "-3.40283e+38"; // unrepresentable - 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); - assert(v == -HUGE_VALF); - } -} - -template , int> = 0> -void test_inf(ios& instr, const my_facet& f) { - Flt v = 0.0; - { - v = -1; - const char str[] = "1.79779e+309"; // unrepresentable - 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); - assert(v == HUGE_VAL); - } - { - v = -1; - const char str[] = "-1.79779e+308"; // unrepresentable - 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); - assert(v == -HUGE_VAL); - } -} - -template , int> = 0> -void test_sep(ios& instr, const my_facet& f) { - Flt v = -1; - const char str[] = "456_78_9;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 == 456789.5); -} - -template , int> = 0> -void test_sep(ios& instr, const my_facet& f) { - Flt 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 + sizeof(str) - 1); - assert(err == instr.goodbit); - assert(v == 123456789.125); -} - template void test() { const my_facet f(1); @@ -172,10 +105,39 @@ void test() { assert(err == instr.goodbit); assert(v == 2); } - test_inf(instr, f); + + { + 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)); - test_sep(instr, f); + { + 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_" From b44b9a0813e0fa5d0694476650efc6a3e9089cbe Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 29 Jan 2023 00:07:48 +0800 Subject: [PATCH 11/57] Address grouping and multi-def issues --- stl/inc/xlocnum | 785 +++++++++--------- .../LWG2318_num_get_floating_point/test.cpp | 113 +++ 2 files changed, 517 insertions(+), 381 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 043efcc69c..723f0b1bcb 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -251,6 +251,380 @@ __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id; #endif // __clang__ #endif // !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) +// TRANSITION, ABI: Sentinel value used by num_get::do_get() to enable correct "V2" behavior +#define _ENABLE_V2_BEHAVIOR 1000000000 + +template +int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) { + // get floating-point field from [_First, _Last) into _Ac + const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); + char* _Ptr = _Ac; + bool _Bad = false; + bool _Sticky = false; + + constexpr int _Offset_dec_digit_end = 10; + constexpr int _Offset_hex_digit_end = 22; + constexpr int _Offset_neg_sign = 22; + constexpr int _Offset_pos_sign = _Offset_neg_sign + 1; + constexpr int _Offset_upper_x = 24; + constexpr int _Offset_lower_x = _Offset_upper_x + 1; + constexpr int _Offset_upper_p = 26; + constexpr int _Offset_lower_p = _Offset_upper_p + 1; + constexpr int _Offset_upper_e = 0xE; + constexpr int _Offset_lower_e = 0xE + 0x10 - 10; + 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); + + const _Elem _Positive_sign = _Atoms[_Offset_pos_sign]; + const _Elem _Negative_sign = _Atoms[_Offset_neg_sign]; + const _Elem _Upper_e = _Atoms[_Offset_upper_e]; + const _Elem _Lower_e = _Atoms[_Offset_lower_e]; + const _Elem _Zero_ch = _Atoms[0]; + + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; + } + } + + char* const _Leading = _Ptr; // remember backstop + *_Ptr++ = '0'; // backstop carries from sticky bit + + bool _Seendigit = false; // seen a digit in input + int _Significant = 0; // number of significant digits + size_t _Idx; + + const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); + + bool _Parse_hex = false; + bool _Has_dec_leading_zero = false; + if (_First != _Last && *_First == _Zero_ch) { + ++_First; + if (_First == _Last) { // "0" only + *_Ptr = '\0'; + return 0; + } + 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 + } else { + _Has_dec_leading_zero = true; + } + } + + const string _Grouping = _Punct_fac.grouping(); + + if (_Parse_hex) { + int _Power_of_16 = 0; + + *_Ptr++ = 'x'; + + if (_Grouping.empty()) { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Max_sig_dig <= _Significant) { + ++_Power_of_16; // just scale by 16 + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + } + } else { + const _Elem _Kseparator = _Punct_fac.thousands_sep(); + string _Groups(1, '\0'); + size_t _Group_index = 0; + + for (; _First != _Last; ++_First) { + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) + < _Offset_hex_digit_end) { // got a digit, add to group size + _Seendigit = true; + if (_Max_sig_dig <= _Significant) { + ++_Power_of_16; // just scale by 16 + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + + if (_Groups[_Group_index] != CHAR_MAX) { + ++_Groups[_Group_index]; + } + } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { + break; // not a group separator, done + } else { // add a new group to _Groups string + _Groups.push_back('\0'); + ++_Group_index; + } + } + + if (_Group_index != 0) { + if (_Groups[_Group_index] > '\0') { + ++_Group_index; // add trailing group to group count + } else { + _Bad = true; // trailing separator, fail + } + } + + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); + char _Current_grouping_count; + while (!_Bad && _Group_index > 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; + } + + --_Group_index; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) + || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { + _Bad = true; // bad group size, fail + } + // 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++ = _CSTD localeconv()->decimal_point[0]; + ++_First; + } + + if (_Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { + --_Power_of_16; // just count leading fraction zeros + } + + if (_Power_of_16 < 0) { // put one back + *_Ptr++ = '0'; + ++_Power_of_16; + } + } + + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant < _Max_sig_dig) { // save a significant fraction digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + } + + if (_Seendigit && _First != _Last + && (*_First == _Atoms[_Offset_lower_p] + || *_First == _Atoms[_Offset_upper_p])) { // 'p' or 'P', collect exponent + *_Ptr++ = 'p'; + ++_First; + _Seendigit = false; + _Significant = 0; + + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; + } + } + + for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros + _Seendigit = true; + } + + if (_Seendigit) { + *_Ptr++ = '0'; // put one back + } + + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + } + } + + if (_Bad || !_Seendigit) { + _Ptr = _Ac; // roll back pointer to indicate failure + } + + *_Ptr = '\0'; + *_Phexexp = _Power_of_16; // power of 16 multiplier + return 0; + } else { + int _Power_of_10 = 0; + + if (_Grouping.empty()) { + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky + ++_Power_of_10; + if (0 < _Idx) { + _Sticky = 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 = _Punct_fac.thousands_sep(); + string _Groups(1, '\0'); + size_t _Group_index = 0; + + if (_Has_dec_leading_zero) { + ++_Groups[_Group_index]; + } + + for (; _First != _Last; ++_First) { + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) + < _Offset_dec_digit_end) { // got a digit, add to group size + _Seendigit = true; + if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky + ++_Power_of_10; + if (0 < _Idx) { + _Sticky = true; + } + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + + if (_Groups[_Group_index] != CHAR_MAX) { + ++_Groups[_Group_index]; + } + } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { + break; // not a group separator, done + } else { // add a new group to _Groups string + _Groups.push_back('\0'); + ++_Group_index; + } + } + + if (_Group_index != 0) { + if ('\0' < _Groups[_Group_index]) { + ++_Group_index; // add trailing group to group count + } else { + _Bad = true; // trailing separator, fail + } + } + + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); + char _Current_grouping_count; + while (!_Bad && _Group_index > 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; + } + + --_Group_index; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) + || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { + _Bad = true; // bad group size, fail + } + // group size okay, advance to next test + } + } + + const char _Decimal_point = _CSTD localeconv()->decimal_point[0]; + if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . + *_Ptr++ = _Decimal_point; + ++_First; + } + + if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { + --_Power_of_10; // just count leading fraction zeros + } + + if (_Power_of_10 < 0) { // put one back + *_Ptr++ = '0'; + ++_Power_of_10; + } + } + + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_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 + } + } + + if (_Sticky) { // increment ls digit in memory of those lost + char* _Px = _Ptr; + while (--_Px != _Leading) { // add in carry + if (*_Px != _Decimal_point) { // not decimal point + if (*_Px != '9') { // carry stops here + ++*_Px; + break; + } + + *_Px = '0'; // propagate carry + } + } + + if (_Px == _Leading) { // change "999..." to "1000..." and scale _Power_of_10 + *_Px = '1'; + ++_Power_of_10; + } + } + + if (_Seendigit && _First != _Last + && (*_First == _Lower_e || *_First == _Upper_e)) { // 'e' or 'E', collect exponent + *_Ptr++ = 'e'; + ++_First; + _Seendigit = false; + _Significant = 0; + + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; + } + } + + for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros + _Seendigit = true; + } + + if (_Seendigit) { + *_Ptr++ = '0'; // put one back + } + + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } + } + } + + if (_Bad || !_Seendigit) { + _Ptr = _Ac; // roll back pointer to indicate failure + } + + *_Ptr = '\0'; + return _Power_of_10; + } +} + _EXPORT_STD extern "C++" template >> class num_get : public locale::facet { // facet for converting text to encoded numbers public: @@ -540,10 +914,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) @@ -552,7 +922,7 @@ protected: _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _Getffldx(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field + const int _Base = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0f; @@ -579,7 +949,7 @@ protected: _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _Getffldx(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field + const int _Base = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0; @@ -642,7 +1012,8 @@ protected: private: int __CLRCALL_OR_CDECL _Getifld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base::fmtflags _Basefield, - const locale& _Loc) const { // get integer field from [_First, _Last) into _Ac + const locale& _Loc) const { + // get integer field from [_First, _Last) into _Ac const auto& _Punct_fac = _STD use_facet>(_Loc); const string _Grouping = _Punct_fac.grouping(); const _Elem _Kseparator = _Grouping.empty() ? _Elem{} : _Punct_fac.thousands_sep(); @@ -697,10 +1068,10 @@ 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; + size_t _Group_index = 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) { @@ -709,33 +1080,40 @@ private: } _Seendigit = true; - if (_Groups[_Group] != CHAR_MAX) { - ++_Groups[_Group]; + if (_Groups[_Group_index] != CHAR_MAX) { + ++_Groups[_Group_index]; } - } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { + } else if (_Groups[_Group_index] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) { break; // not a group separator, done } else { // add a new group to _Groups string _Groups.push_back('\0'); - ++_Group; + ++_Group_index; } } - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count + if (_Group_index != 0) { + if (_Groups[_Group_index] > '\0') { + ++_Group_index; // 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 && _Group_index > 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; + } + + --_Group_index; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) + || (_Group_index == 0 && _Groups[_Group_index] > _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) { @@ -751,372 +1129,17 @@ private: 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 - return _Getffldx(_Ac, _First, _Last, _Iosbase, _Phexexp); + return _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, _Phexexp); } 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 - const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); - const string _Grouping = _Punct_fac.grouping(); - char* _Ptr = _Ac; - bool _Bad = false; - bool _Sticky = false; - - constexpr int _Offset_dec_digit_end = 10; - constexpr int _Offset_hex_digit_end = 22; - constexpr int _Offset_neg_sign = 22; - constexpr int _Offset_pos_sign = _Offset_neg_sign + 1; - constexpr int _Offset_upper_x = 24; - constexpr int _Offset_lower_x = _Offset_upper_x + 1; - constexpr int _Offset_upper_p = 26; - constexpr int _Offset_lower_p = _Offset_upper_p + 1; - constexpr int _Offset_upper_e = 0xE; - constexpr int _Offset_lower_e = 0xE + 0x10 - 10; - 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); - - const _Elem _Positive_sign = _Atoms[_Offset_pos_sign]; - const _Elem _Negative_sign = _Atoms[_Offset_neg_sign]; - const _Elem _Upper_e = _Atoms[_Offset_upper_e]; - const _Elem _Lower_e = _Atoms[_Offset_lower_e]; - const _Elem _Zero_ch = _Atoms[0]; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - char* const _Leading = _Ptr; // remember backstop - *_Ptr++ = '0'; // backstop carries from sticky bit - - bool _Seendigit = false; // seen a digit in input - int _Significant = 0; // number of significant digits - size_t _Idx; - - const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - - bool _Parse_hex = false; - bool _Has_dec_leading_zero = false; - if (_First != _Last && *_First == _Zero_ch) { - ++_First; - if (_First == _Last) { // "0" only - *_Ptr = '\0'; - return 0; - } - 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 - } else { - _Has_dec_leading_zero = true; - } - } - - const char* _Current_grouping = &_Grouping[0]; - - if (_Parse_hex) { - int _Power_of_16 = 0; - - *_Ptr++ = 'x'; - - if (*_Current_grouping == CHAR_MAX || *_Current_grouping <= '\0') { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { - ++_Power_of_16; // just scale by 16 - } 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; - - for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) - < _Offset_hex_digit_end) { // got a digit, add to group size - _Seendigit = true; - if (_Max_sig_dig <= _Significant) { - ++_Power_of_16; // just scale by 16 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - - 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; - } - } - - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail - } - } - - while (!_Bad && 0 < _Group) { - if (*_Current_grouping == CHAR_MAX) { - break; // end of grouping constraints to check - } - - if ((0 < --_Group && *_Current_grouping != _Groups[_Group]) - || (0 == _Group && *_Current_grouping < _Groups[_Group])) { - _Bad = true; // bad group size, fail - } else if ('\0' < _Current_grouping[1]) { - ++_Current_grouping; // 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++ = _CSTD localeconv()->decimal_point[0]; - ++_First; - } - - if (_Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Power_of_16; // just count leading fraction zeros - } - - if (_Power_of_16 < 0) { // put one back - *_Ptr++ = '0'; - ++_Power_of_16; - } - } - - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _Max_sig_dig) { // save a significant fraction digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - - if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Offset_lower_p] - || *_First == _Atoms[_Offset_upper_p])) { // 'p' or 'P', collect exponent - *_Ptr++ = 'p'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros - _Seendigit = true; - } - - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } - - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } - - *_Ptr = '\0'; - *_Phexexp = _Power_of_16; // power of 16 multiplier - return 0; - } else { - int _Power_of_10 = 0; - - if (*_Current_grouping == CHAR_MAX || *_Current_grouping <= '\0') { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Power_of_10; - if (0 < _Idx) { - _Sticky = 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; - - if (_Has_dec_leading_zero) { - ++_Groups[_Group]; - } - - for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) - < _Offset_dec_digit_end) { // got a digit, add to group size - _Seendigit = true; - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Power_of_10; - if (0 < _Idx) { - _Sticky = true; - } - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - - 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; - } - } - - if (_Group != 0) { - if ('\0' < _Groups[_Group]) { - ++_Group; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail - } - } - - while (!_Bad && 0 < _Group) { - if (*_Current_grouping == CHAR_MAX) { - break; // end of grouping constraints to check - } - - if ((0 < --_Group && *_Current_grouping != _Groups[_Group]) - || (0 == _Group && *_Current_grouping < _Groups[_Group])) { - _Bad = true; // bad group size, fail - } else if ('\0' < _Current_grouping[1]) { - ++_Current_grouping; // group size okay, advance to next test - } - } - } - - const char _Decimal_point = _CSTD localeconv()->decimal_point[0]; - if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . - *_Ptr++ = _Decimal_point; - ++_First; - } - - if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Power_of_10; // just count leading fraction zeros - } - - if (_Power_of_10 < 0) { // put one back - *_Ptr++ = '0'; - ++_Power_of_10; - } - } - - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_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 - } - } - - if (_Sticky) { // increment ls digit in memory of those lost - char* _Px = _Ptr; - while (--_Px != _Leading) { // add in carry - if (*_Px != _Decimal_point) { // not decimal point - if (*_Px != '9') { // carry stops here - ++*_Px; - break; - } - - *_Px = '0'; // propagate carry - } - } - - if (_Px == _Leading) { // change "999..." to "1000..." and scale _Power_of_10 - *_Px = '1'; - ++_Power_of_10; - } - } - - if (_Seendigit && _First != _Last - && (*_First == _Lower_e || *_First == _Upper_e)) { // 'e' or 'E', collect exponent - *_Ptr++ = 'e'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros - _Seendigit = true; - } - - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } - - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } - - *_Ptr = '\0'; - return _Power_of_10; - } + return _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, _Phexexp); } +}; #undef _ENABLE_V2_BEHAVIOR -}; #ifdef __clang__ #pragma clang diagnostic push diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index ab72ff1905..3f7fcacb78 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -22,6 +22,16 @@ class my_facet : public num_get { explicit my_facet(size_t refs = 0) : num_get(refs) {} }; +class mid_zero_numpunct : public numpunct { +public: + mid_zero_numpunct() : numpunct() {} + +protected: + virtual string do_grouping() const { + return "\1\0\2"s; + } +}; + class my_numpunct : public numpunct { public: my_numpunct() : numpunct() {} @@ -176,10 +186,113 @@ void test() { assert(v == 0x125p-1); assert(str_instr.good()); } + + // Also test non-ending unlimited grouping + instr.imbue(locale(locale(), new mid_zero_numpunct)); + { + 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 +void test_int_grouping() { + const my_facet f(1); + ios instr(nullptr); + instr.imbue(locale(locale(), new mid_zero_numpunct)); + + Integer v = 0; + { + v = -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 == 1729); + } + { + v = -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 == 172229); + } + { + v = -1; + const char sep_str[] = "0x17,2,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + + instr.flags(std::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 == 0x1729); + } + { + v = -1; + const char sep_str[] = "0x17,222,9"; + const size_t len = sizeof(sep_str) - 1; + ios_base::iostate err = instr.goodbit; + + instr.flags(std::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 == 0x172229); + } } int main() { test(); test(); test(); + + test_int_grouping(); + test_int_grouping(); + test_int_grouping(); + test_int_grouping(); + test_int_grouping(); } From 38151c185e28c9c331114f3a2753785903858c4f Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 29 Jan 2023 00:36:24 +0800 Subject: [PATCH 12/57] Revert unintended line-breaking --- stl/inc/xlocnum | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 723f0b1bcb..5025e0311e 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1012,8 +1012,7 @@ protected: private: int __CLRCALL_OR_CDECL _Getifld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base::fmtflags _Basefield, - const locale& _Loc) const { - // get integer field from [_First, _Last) into _Ac + const locale& _Loc) const { // get integer field from [_First, _Last) into _Ac const auto& _Punct_fac = _STD use_facet>(_Loc); const string _Grouping = _Punct_fac.grouping(); const _Elem _Kseparator = _Grouping.empty() ? _Elem{} : _Punct_fac.thousands_sep(); From f7eb879d54a4b11d1ea342d5c5549f8067e85578 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 29 Jan 2023 00:52:05 +0800 Subject: [PATCH 13/57] Don't default-initialize `_Current_grouping_count` --- stl/inc/xlocnum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 5025e0311e..30ec64dc40 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -372,7 +372,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I const char* _Grouping_iter = _Grouping.data(); const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - char _Current_grouping_count; + char _Current_grouping_count = '\0'; while (!_Bad && _Group_index > 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 @@ -519,7 +519,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I const char* _Grouping_iter = _Grouping.data(); const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - char _Current_grouping_count; + char _Current_grouping_count = '\0'; while (!_Bad && _Group_index > 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 From ac30e4cf60e6817d02a27594145d073ec33ce8e4 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 29 Jan 2023 01:18:57 +0800 Subject: [PATCH 14/57] Avoid signedness issues --- .../LWG2318_num_get_floating_point/test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp index 3f7fcacb78..78f9d0a0fe 100644 --- a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2318_num_get_floating_point/test.cpp @@ -240,27 +240,27 @@ void test_int_grouping() { Integer v = 0; { - v = -1; + 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 == 1729); + assert(v == static_cast(1729)); } { - v = -1; + 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 == 172229); + assert(v == static_cast(172229)); } { - v = -1; + 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; @@ -269,10 +269,10 @@ void test_int_grouping() { 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); + assert(v == static_cast(0x1729)); } { - v = -1; + 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; @@ -281,7 +281,7 @@ void test_int_grouping() { 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); + assert(v == static_cast(0x172229)); } } From 18696e1594a6be3c14c0ff2e237394ccd31447ae Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 29 Jan 2023 01:36:45 +0800 Subject: [PATCH 15/57] Fix typo in the directory name! --- tests/std/test.lst | 2 +- .../env.lst | 0 .../test.cpp | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/std/tests/{LWG2318_num_get_floating_point => LWG2381_num_get_floating_point}/env.lst (100%) rename tests/std/tests/{LWG2318_num_get_floating_point => LWG2381_num_get_floating_point}/test.cpp (100%) diff --git a/tests/std/test.lst b/tests/std/test.lst index b6eddb37fa..fba15a1d42 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -218,7 +218,7 @@ tests\GH_003022_substr_allocator tests\GH_003105_piecewise_densities tests\GH_003119_error_category_ctor tests\GH_003246_cmath_narrowing -tests\LWG2318_num_get_floating_point +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/LWG2318_num_get_floating_point/env.lst b/tests/std/tests/LWG2381_num_get_floating_point/env.lst similarity index 100% rename from tests/std/tests/LWG2318_num_get_floating_point/env.lst rename to tests/std/tests/LWG2381_num_get_floating_point/env.lst diff --git a/tests/std/tests/LWG2318_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp similarity index 100% rename from tests/std/tests/LWG2318_num_get_floating_point/test.cpp rename to tests/std/tests/LWG2381_num_get_floating_point/test.cpp From 1c63310052eb953e5dbaedfa062f513f8ad279e7 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 30 Jan 2023 11:27:55 +0800 Subject: [PATCH 16/57] Small cleanups - eliminate some Yoda conditions - reduce the scope of `_Sticky` - compactify the use of `_Has_dec_leading_zero` --- stl/inc/xlocnum | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 30ec64dc40..f8708ab4d3 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -260,7 +260,6 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); char* _Ptr = _Ac; bool _Bad = false; - bool _Sticky = false; constexpr int _Offset_dec_digit_end = 10; constexpr int _Offset_hex_digit_end = 22; @@ -461,13 +460,14 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I return 0; } else { int _Power_of_10 = 0; + bool _Sticky = false; if (_Grouping.empty()) { for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; _Seendigit = true, (void) ++_First) { if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky ++_Power_of_10; - if (0 < _Idx) { + if (_Idx > 0) { _Sticky = true; } } else if (_Idx != 0 || _Significant != 0) { // save a significant digit @@ -477,20 +477,16 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I } } else { // grouping specified, gather digits and group sizes const _Elem _Kseparator = _Punct_fac.thousands_sep(); - string _Groups(1, '\0'); + string _Groups(1, static_cast(_Has_dec_leading_zero)); size_t _Group_index = 0; - if (_Has_dec_leading_zero) { - ++_Groups[_Group_index]; - } - for (; _First != _Last; ++_First) { if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end) { // got a digit, add to group size _Seendigit = true; if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky ++_Power_of_10; - if (0 < _Idx) { + if (_Idx > 0) { _Sticky = true; } } else if (_Idx != 0 || _Significant != 0) { // save a significant digit @@ -510,7 +506,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I } if (_Group_index != 0) { - if ('\0' < _Groups[_Group_index]) { + if (_Groups[_Group_index] > '\0') { ++_Group_index; // add trailing group to group count } else { _Bad = true; // trailing separator, fail From 5fd6dd6a91a8b4540aec4c3ba1472ed49a5c1943 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 10:44:41 +0800 Subject: [PATCH 17/57] Address GH-3376 --- stl/inc/xlocnum | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index f8708ab4d3..d839cf9b42 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -572,9 +572,8 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I } } - if (_Px == _Leading) { // change "999..." to "1000..." and scale _Power_of_10 + if (_Px == _Leading) { // change "09..." to "10...", no need to increase _Power_of_10 *_Px = '1'; - ++_Power_of_10; } } From b4162ee3cc71919c2c2d3251f25ee26c4c38e019 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 11:43:23 +0800 Subject: [PATCH 18/57] Test coverage for GH-3376 --- .../LWG2381_num_get_floating_point/test.cpp | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 78f9d0a0fe..8262e1370d 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -285,6 +285,124 @@ void test_int_grouping() { } } +// Also test GH-3376 : Incorrect result when parsing 9.999999... +template +void test_gh3376() { + // Ensure that "0.0999....999" is still correctly parsed. + { + istringstream stream("0.09" + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(stream)); + assert((ostringstream{} << x).str() == "0.1"); + } + { + istringstream stream("-0.09" + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(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(bool(stream)); + assert((ostringstream{} << x).str() == "9"); + } + { + istringstream stream("-8." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(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(bool(stream)); + assert((ostringstream{} << x).str() == "1"); + } + { + istringstream stream("-0." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(stream)); + assert((ostringstream{} << x).str() == "-1"); + } + { + istringstream stream("9." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(stream)); + assert((ostringstream{} << x).str() == "10"); + } + { + istringstream stream("-9." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(stream)); + assert((ostringstream{} << x).str() == "-10"); + } + + // Ensure that huge "999...999.999...999" represantations are correctly parsed. + { + istringstream stream(string(38, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(stream)); + assert((ostringstream{} << x).str() == "1e+38"); + } + { + istringstream stream("-" + string(38, '9') + "." + string(800, '9')); + Flt x = 0.0; + + stream >> x; + assert(bool(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(bool(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(bool(stream)); + assert((ostringstream{} << x).str() == "-1e+308"); + } + } +} + int main() { test(); test(); @@ -295,4 +413,8 @@ int main() { test_int_grouping(); test_int_grouping(); test_int_grouping(); + + test_gh3376(); + test_gh3376(); + test_gh3376(); } From 37bf2183f584a15df2931e78d5987293bc7bfbd9 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 11:50:11 +0800 Subject: [PATCH 19/57] Clang-format: strange empty line --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 8262e1370d..2dd7b42b03 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -357,7 +357,7 @@ void test_gh3376() { assert(bool(stream)); assert((ostringstream{} << x).str() == "-10"); } - + // Ensure that huge "999...999.999...999" represantations are correctly parsed. { istringstream stream(string(38, '9') + "." + string(800, '9')); From c05bb2ec1a679526373b682c875677b34f67220d Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 15:14:15 +0800 Subject: [PATCH 20/57] Unify hex/dec branchs, address GH-3375 and GH-3378 Co-authored-by: statementreply --- stl/inc/xlocnum | 395 +++++++++++++++--------------------------------- 1 file changed, 125 insertions(+), 270 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index d839cf9b42..13fedbfb93 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -259,7 +259,6 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I // get floating-point field from [_First, _Last) into _Ac const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); char* _Ptr = _Ac; - bool _Bad = false; constexpr int _Offset_dec_digit_end = 10; constexpr int _Offset_hex_digit_end = 22; @@ -278,9 +277,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I const _Elem _Positive_sign = _Atoms[_Offset_pos_sign]; const _Elem _Negative_sign = _Atoms[_Offset_neg_sign]; - const _Elem _Upper_e = _Atoms[_Offset_upper_e]; - const _Elem _Lower_e = _Atoms[_Offset_lower_e]; - const _Elem _Zero_ch = _Atoms[0]; + const _Elem _Zero_wc = _Atoms[0]; if (_First != _Last) { if (*_First == _Positive_sign) { // gather plus sign @@ -295,15 +292,9 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I char* const _Leading = _Ptr; // remember backstop *_Ptr++ = '0'; // backstop carries from sticky bit - bool _Seendigit = false; // seen a digit in input - int _Significant = 0; // number of significant digits - size_t _Idx; - - const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - - bool _Parse_hex = false; - bool _Has_dec_leading_zero = false; - if (_First != _Last && *_First == _Zero_ch) { + bool _Parse_hex = false; + char _Initial_dec_leading_zero = '\0'; + if (_First != _Last && *_First == _Zero_wc) { ++_First; if (_First == _Last) { // "0" only *_Ptr = '\0'; @@ -312,161 +303,46 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I 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 { - _Has_dec_leading_zero = true; + ++_Initial_dec_leading_zero; } } - const string _Grouping = _Punct_fac.grouping(); - - if (_Parse_hex) { - int _Power_of_16 = 0; - - *_Ptr++ = 'x'; - - if (_Grouping.empty()) { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { - ++_Power_of_16; // just scale by 16 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } else { - const _Elem _Kseparator = _Punct_fac.thousands_sep(); - string _Groups(1, '\0'); - size_t _Group_index = 0; - - for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) - < _Offset_hex_digit_end) { // got a digit, add to group size - _Seendigit = true; - if (_Max_sig_dig <= _Significant) { - ++_Power_of_16; // just scale by 16 - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - - if (_Groups[_Group_index] != CHAR_MAX) { - ++_Groups[_Group_index]; - } - } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { - break; // not a group separator, done - } else { // add a new group to _Groups string - _Groups.push_back('\0'); - ++_Group_index; - } - } - - if (_Group_index != 0) { - if (_Groups[_Group_index] > '\0') { - ++_Group_index; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail - } - } - - const char* _Grouping_iter = _Grouping.data(); - const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - char _Current_grouping_count = '\0'; - while (!_Bad && _Group_index > 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; - } - - --_Group_index; - if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) - && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) - || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { - _Bad = true; // bad group size, fail - } - // 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++ = _CSTD localeconv()->decimal_point[0]; - ++_First; - } - - if (_Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Power_of_16; // just count leading fraction zeros - } + const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - if (_Power_of_16 < 0) { // put one back - *_Ptr++ = '0'; - ++_Power_of_16; - } - } + size_t _Idx; + bool _Sticky = false; + bool _Seendigit = false; // seen a digit in input + bool _Bad = false; + int _Significant = 0; // number of significant digits + int _Power_of_rep_base = 0; // power of 10 or 16 - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; + const size_t _Offset_digit_end = _Parse_hex ? _Offset_hex_digit_end : _Offset_dec_digit_end; + const string _Grouping = _Punct_fac.grouping(); + if (_Grouping.empty()) { + for (; _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 + if (_Max_sig_dig <= _Significant) { + ++_Power_of_rep_base; // just scale by 10 or 16 + if (_Idx > 0) { + _Sticky = true; + } + } else if (_Idx != 0 || _Significant != 0) { // save a significant digit *_Ptr++ = _Src[_Idx]; ++_Significant; } } + } else { + const _Elem _Kseparator = _Punct_fac.thousands_sep(); + string _Groups(1, _Initial_dec_leading_zero); + size_t _Group_index = 0; - if (_Seendigit && _First != _Last - && (*_First == _Atoms[_Offset_lower_p] - || *_First == _Atoms[_Offset_upper_p])) { // 'p' or 'P', collect exponent - *_Ptr++ = 'p'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros + for (; _First != _Last; ++_First) { + if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end) { // got a digit, add to group size _Seendigit = true; - } - - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } - - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_hex_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } - - *_Ptr = '\0'; - *_Phexexp = _Power_of_16; // power of 16 multiplier - return 0; - } else { - int _Power_of_10 = 0; - bool _Sticky = false; - - if (_Grouping.empty()) { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Power_of_10; + if (_Max_sig_dig <= _Significant) { + ++_Power_of_rep_base; // just scale by 10 or 16 if (_Idx > 0) { _Sticky = true; } @@ -474,149 +350,128 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I *_Ptr++ = _Src[_Idx]; ++_Significant; } - } - } else { // grouping specified, gather digits and group sizes - const _Elem _Kseparator = _Punct_fac.thousands_sep(); - string _Groups(1, static_cast(_Has_dec_leading_zero)); - size_t _Group_index = 0; - - for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) - < _Offset_dec_digit_end) { // got a digit, add to group size - _Seendigit = true; - if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky - ++_Power_of_10; - if (_Idx > 0) { - _Sticky = true; - } - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - if (_Groups[_Group_index] != CHAR_MAX) { - ++_Groups[_Group_index]; - } - } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { - break; // not a group separator, done - } else { // add a new group to _Groups string - _Groups.push_back('\0'); - ++_Group_index; + if (_Groups[_Group_index] != CHAR_MAX) { + ++_Groups[_Group_index]; } + } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { + break; // not a group separator, done + } else { // add a new group to _Groups string + _Groups.push_back('\0'); + ++_Group_index; } + } - if (_Group_index != 0) { - if (_Groups[_Group_index] > '\0') { - ++_Group_index; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail - } + if (_Group_index != 0) { + if (_Groups[_Group_index] > '\0') { + ++_Group_index; // add trailing group to group count + } else { + _Bad = true; // trailing separator, fail } + } - const char* _Grouping_iter = _Grouping.data(); - const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - char _Current_grouping_count = '\0'; - while (!_Bad && _Group_index > 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; - } + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); + char _Current_grouping_count = '\0'; + while (!_Bad && _Group_index > 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; + } - --_Group_index; - if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) - && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) - || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { - _Bad = true; // bad group size, fail - } - // group size okay, advance to next test + --_Group_index; + if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) + && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) + || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { + _Bad = true; // bad group size, fail } + // group size okay, advance to next test } + } - const char _Decimal_point = _CSTD localeconv()->decimal_point[0]; - if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add . - *_Ptr++ = _Decimal_point; - ++_First; + if (_Parse_hex && _Seendigit && _Significant == 0) { + *_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++ = _Decimal_point; + ++_First; + } + + if ((_Parse_hex || *_Phexexp != _ENABLE_V2_BEHAVIOR) && _Significant == 0) { // 0000. so far + for (; _First != _Last && *_First == _Zero_wc; _Seendigit = true, (void) ++_First) { + --_Power_of_rep_base; // just count leading fraction zeros } - if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Zero_ch; _Seendigit = true, (void) ++_First) { - --_Power_of_10; // just count leading fraction zeros - } + if (_Power_of_rep_base < 0) { // put one back + *_Ptr++ = '0'; + ++_Power_of_rep_base; + } + } - if (_Power_of_10 < 0) { // put one back - *_Ptr++ = '0'; - ++_Power_of_10; - } + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant < _Max_sig_dig) { // save a significant fraction digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; + } else if (_Idx > 0) { + _Sticky = true; // just update _Sticky } + } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_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 - } + if (_Sticky) { // increment ls 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; } + } - if (_Sticky) { // increment ls digit in memory of those lost - char* _Px = _Ptr; - while (--_Px != _Leading) { // add in carry - if (*_Px != _Decimal_point) { // not decimal point - if (*_Px != '9') { // carry stops here - ++*_Px; - break; - } + 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 - *_Px = '0'; // propagate carry - } - } + if (_Seendigit && _First != _Last && (*_First == _Lower_exp_wc || *_First == _Upper_exp_wc)) { // collect exponent + *_Ptr++ = _Parse_hex ? 'p' : 'e'; + ++_First; + _Seendigit = false; + _Significant = 0; - if (_Px == _Leading) { // change "09..." to "10...", no need to increase _Power_of_10 - *_Px = '1'; + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; } } - if (_Seendigit && _First != _Last - && (*_First == _Lower_e || *_First == _Upper_e)) { // 'e' or 'E', collect exponent - *_Ptr++ = 'e'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - for (; _First != _Last && *_First == _Zero_ch; ++_First) { // strip leading zeros - _Seendigit = true; - } + for (; _First != _Last && *_First == _Zero_wc; ++_First) { // strip leading zeros + _Seendigit = true; + } - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } + if (_Seendigit) { + *_Ptr++ = '0'; // put one back + } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } + for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + _Seendigit = true, (void) ++_First) { + if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit + *_Ptr++ = _Src[_Idx]; + ++_Significant; } } + } - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } + if (_Bad || !_Seendigit) { + _Ptr = _Ac; // roll back pointer to indicate failure + } - *_Ptr = '\0'; - return _Power_of_10; + if (_Parse_hex) { + *_Phexexp = _Power_of_rep_base; // power of 16 multiplier + return 0; + } else { + return _Power_of_rep_base; // power of 10 multiplier } } From 877dcd22eff40862c77041d2c5b4f12ab905175e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 15:31:02 +0800 Subject: [PATCH 21/57] `_Leading` is no longer useful, good thing --- stl/inc/xlocnum | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 13fedbfb93..a3848db160 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -255,7 +255,7 @@ __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id; #define _ENABLE_V2_BEHAVIOR 1000000000 template -int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) { +int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) { // get floating-point field from [_First, _Last) into _Ac const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); char* _Ptr = _Ac; @@ -289,8 +289,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I } } - char* const _Leading = _Ptr; // remember backstop - *_Ptr++ = '0'; // backstop carries from sticky bit + *_Ptr++ = '0'; // backstop carries from sticky bit bool _Parse_hex = false; char _Initial_dec_leading_zero = '\0'; @@ -341,7 +340,7 @@ int _Parse_fp_with_ios_base(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _I for (; _First != _Last; ++_First) { if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end) { // got a digit, add to group size _Seendigit = true; - if (_Max_sig_dig <= _Significant) { + if (_Significant >= _Max_sig_dig) { ++_Power_of_rep_base; // just scale by 10 or 16 if (_Idx > 0) { _Sticky = true; From 6ca7caca4349e48335dcbdc6967b5e1791fa9a97 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 15:35:28 +0800 Subject: [PATCH 22/57] `*_Ptr = '\0';` was accidently dropped --- stl/inc/xlocnum | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index a3848db160..04a3602302 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -466,6 +466,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba _Ptr = _Ac; // roll back pointer to indicate failure } + *_Ptr = '\0'; if (_Parse_hex) { *_Phexexp = _Power_of_rep_base; // power of 16 multiplier return 0; From 19f635e65c4c347d613ef1f923d29858241ef98f Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 15:57:38 +0800 Subject: [PATCH 23/57] Make `_Offset_*` constants have type `size_t` --- stl/inc/xlocnum | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 04a3602302..bb3544a5e4 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -260,16 +260,16 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); char* _Ptr = _Ac; - constexpr int _Offset_dec_digit_end = 10; - constexpr int _Offset_hex_digit_end = 22; - constexpr int _Offset_neg_sign = 22; - constexpr int _Offset_pos_sign = _Offset_neg_sign + 1; - constexpr int _Offset_upper_x = 24; - constexpr int _Offset_lower_x = _Offset_upper_x + 1; - constexpr int _Offset_upper_p = 26; - constexpr int _Offset_lower_p = _Offset_upper_p + 1; - constexpr int _Offset_upper_e = 0xE; - constexpr int _Offset_lower_e = 0xE + 0x10 - 10; + 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 = _Offset_neg_sign + 1; + constexpr size_t _Offset_upper_x = 24; + constexpr size_t _Offset_lower_x = _Offset_upper_x + 1; + constexpr size_t _Offset_upper_p = 26; + constexpr size_t _Offset_lower_p = _Offset_upper_p + 1; + constexpr size_t _Offset_upper_e = 0xE; + constexpr size_t _Offset_lower_e = 0xE + 0x10 - 10; static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; _Elem _Atoms[sizeof(_Src)]; const ctype<_Elem>& _Ctype_fac = _STD use_facet>(_Iosbase.getloc()); From c299d2b310ca0d97d50df8a6fd22a1c5d810100a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 16:12:40 +0800 Subject: [PATCH 24/57] Clang-format (again) --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index bb3544a5e4..1c4f047bb6 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -270,7 +270,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba constexpr size_t _Offset_lower_p = _Offset_upper_p + 1; constexpr size_t _Offset_upper_e = 0xE; constexpr size_t _Offset_lower_e = 0xE + 0x10 - 10; - static constexpr char _Src[] = "0123456789ABCDEFabcdef-+XxPp"; + 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); From 6bd5d5ae306c90fa53d558dfe679913a873a1f88 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 17:00:44 +0800 Subject: [PATCH 25/57] Fix a copy-pasta to correct handling for hex rep --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 1c4f047bb6..561c6eea81 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -409,7 +409,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba } } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_dec_digit_end; + for (; _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]; From f17b6fe3b8a381b303c9ba6fd38f6e4f312f2229 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 2 Feb 2023 17:55:57 +0800 Subject: [PATCH 26/57] Test coverage for GH-3376 and GH-3378 --- .../LWG2381_num_get_floating_point/test.cpp | 99 ++++++++++++++++++- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 2dd7b42b03..9b4b09d5d2 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -285,9 +285,48 @@ void test_int_grouping() { } } -// Also test GH-3376 : Incorrect result when parsing 9.999999... +// Also test GH-3375 : Incorrect rounding when parsing long hexadecimal floating point numbers just above +// midpoints +// And GH-3376 : Incorrect result when parsing 9.999999... template -void test_gh3376() { +void test_gh3375_gh3376() { + // 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(bool(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(bool(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(bool(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(bool(stream)); + assert((ostringstream{} << hexfloat << x).str() == "-0x1.0000000000001p+0"); + } + } + // Ensure that "0.0999....999" is still correctly parsed. { istringstream stream("0.09" + string(800, '9')); @@ -403,6 +442,53 @@ void test_gh3376() { } } +// Also test GH-3378: : Incorrect rounding when parsing long floating point numbers just below midpoints +template +void test_gh3378() { + { + // 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(bool(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(bool(stream)); + assert((move(os) << x).str() == "-2.2250738585072019e-308"); + } +} + int main() { test(); test(); @@ -414,7 +500,10 @@ int main() { test_int_grouping(); test_int_grouping(); - test_gh3376(); - test_gh3376(); - test_gh3376(); + test_gh3375_gh3376(); + test_gh3375_gh3376(); + test_gh3375_gh3376(); + + test_gh3378(); + test_gh3378(); } From e6a607658583d9a5bca52732d61932f27f9d5b99 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 8 Feb 2023 11:23:43 +0800 Subject: [PATCH 27/57] Update notes for skipping/failure --- tests/libcxx/expected_results.txt | 14 ++++++++------ tests/libcxx/skipped_tests.txt | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a6f952056c..5d69cb6dd9 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -350,11 +350,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 @@ -591,9 +586,12 @@ std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_p # libc++ chose option A for [time.clock.file.members], and we chose option B. std/time/time.clock/time.clock.file/to_from_sys.pass.cpp FAIL -# libc++'s filesystem::path::iterator model bidirectional_iterator, which is not guaranteed by the Standard +# 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 as precise as the Intel 80-bit format; also affected by LWG-2381 +std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long_double.pass.cpp FAIL + # libc++ is missing various Ranges DRs std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp FAIL @@ -614,6 +612,10 @@ std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp FAIL # libc++ is missing various DRs std/language.support/support.limits/support.limits.general/format.version.compile.pass.cpp FAIL +# libc++ doesn't yet implement LWG-2381 (LLVM-60597) +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 yet implement LWG-3670 std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 7f4cea3d97..a4dc259db6 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -350,11 +350,6 @@ containers\sequences\array\array.swap\swap.fail.cpp algorithms\alg.sorting\alg.merge\inplace_merge_comp.pass.cpp algorithms\alg.sorting\alg.min.max\minmax_init_list_comp.pass.cpp -# GH-1259 : wrong field extraction for hexfloats, or special cases like inf -localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_double.pass.cpp -localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_float.pass.cpp -localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_long_double.pass.cpp - # GH-1277 : We don't match numpunct groups correctly in do_get localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_long.pass.cpp @@ -591,9 +586,12 @@ utilities\format\format.formatter\format.formatter.spec\formatter.floating_point # libc++ chose option A for [time.clock.file.members], and we chose option B. time\time.clock\time.clock.file\to_from_sys.pass.cpp -# libc++'s filesystem::path::iterator model bidirectional_iterator, which is not guaranteed by the Standard +# libc++'s filesystem::path::iterator models bidirectional_iterator, which is not guaranteed by the Standard input.output\filesystems\class.path\range_concept_conformance.compile.pass.cpp +# libc++ assumes long double is at least as precise as the Intel 80-bit format; also affected by LWG-2381 +localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_long_double.pass.cpp + # libc++ is missing various Ranges DRs language.support\support.limits\support.limits.general\ranges.version.compile.pass.cpp @@ -614,6 +612,10 @@ strings\basic.string\string.capacity\resize_and_overwrite.pass.cpp # libc++ is missing various DRs language.support\support.limits\support.limits.general\format.version.compile.pass.cpp +# libc++ doesn't yet implement LWG-2381 (LLVM-60597) +localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_double.pass.cpp +localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_float.pass.cpp + # libc++ doesn't yet implement LWG-3670 ranges\range.factories\range.iota.view\iterator\member_typedefs.compile.pass.cpp From 492b1b37f65ed9433291eb45b26657a1f0564d7e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 15 Feb 2023 08:56:38 +0800 Subject: [PATCH 28/57] Drop misreported LLVM-60597 from the comments --- tests/libcxx/expected_results.txt | 2 +- tests/libcxx/skipped_tests.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 3037474887..9bd5ad9a80 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -114,7 +114,7 @@ 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 (LLVM-60597) +# 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 diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index dab482f111..dac15a3218 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -114,7 +114,7 @@ language.support\support.limits\support.limits.general\ranges.version.compile.pa # libc++ is missing various DRs language.support\support.limits\support.limits.general\format.version.compile.pass.cpp -# libc++ doesn't implement LWG-2381 (LLVM-60597) +# libc++ doesn't implement LWG-2381 localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_double.pass.cpp localization\locale.categories\category.numeric\locale.num.get\facet.num.get.members\get_float.pass.cpp From fa90c01f9c9bb3bb3d4548caf0ed24db44d948dd Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:07:33 -0700 Subject: [PATCH 29/57] Update and correct citations. --- stl/inc/xlocnum | 4 ++-- tests/std/tests/VSO_0234888_num_get_overflows/test.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 561c6eea81..bf341fc42b 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -590,7 +590,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 N4928 [facet.num.get.virtuals]/3.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 @@ -1004,7 +1004,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 N4928 [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/std/tests/VSO_0234888_num_get_overflows/test.cpp b/tests/std/tests/VSO_0234888_num_get_overflows/test.cpp index 74934ac3a8..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 N4928 [facet.num.get.virtuals]/3.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 N4928 [facet.num.get.virtuals]/3.3.6 + // See N4944 [facet.num.get.virtuals]/3.9 // * "zero, if the conversion function does not convert the entire field" { istringstream src("-"s); From 220200dcd2273f823f3368df1555fa18a6a75620 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:11:12 -0700 Subject: [PATCH 30/57] Adjust newlines. --- stl/inc/xlocnum | 1 + tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index bf341fc42b..319a2c15e2 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -299,6 +299,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba *_Ptr = '\0'; return 0; } + 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 diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 9b4b09d5d2..362704c600 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -115,7 +115,6 @@ void test() { assert(err == instr.goodbit); assert(v == 2); } - { v = -1; const char* inf_str = is_same_v ? "3.40283e+39" : "1.7979e+309"; // unrepresentable @@ -177,7 +176,6 @@ void test() { assert(err == instr.goodbit); assert(abs(v - 3.14159265358979e+10) / 3.14159265358979e+10 < 1.e-8); } - { v = -1; const char str[] = "0x125p-1 "; From dc3b17a5380388f02073b604ef765c7f6968080c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:11:48 -0700 Subject: [PATCH 31/57] Drop `std::`. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 362704c600..95eb794ded 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -263,7 +263,7 @@ void test_int_grouping() { const size_t len = sizeof(sep_str) - 1; ios_base::iostate err = instr.goodbit; - instr.flags(std::ios_base::fmtflags{}); + 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); @@ -275,7 +275,7 @@ void test_int_grouping() { const size_t len = sizeof(sep_str) - 1; ios_base::iostate err = instr.goodbit; - instr.flags(std::ios_base::fmtflags{}); + 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); From fe659a14e1d0fca25fc37c0a53bce2dbd5b7fb70 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:12:18 -0700 Subject: [PATCH 32/57] Include ``. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 95eb794ded..414efc194a 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include using namespace std; From 42ebf6e218689c1597d6e2e55266ba12726af077 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:13:01 -0700 Subject: [PATCH 33/57] Use `override` by itself. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 414efc194a..5b1cc2109b 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -28,7 +28,7 @@ class mid_zero_numpunct : public numpunct { mid_zero_numpunct() : numpunct() {} protected: - virtual string do_grouping() const { + string do_grouping() const override { return "\1\0\2"s; } }; @@ -38,13 +38,13 @@ class my_numpunct : public numpunct { my_numpunct() : numpunct() {} protected: - virtual char_type do_decimal_point() const { + char_type do_decimal_point() const override { return ';'; } - virtual char_type do_thousands_sep() const { + char_type do_thousands_sep() const override { return '_'; } - virtual string do_grouping() const { + string do_grouping() const override { return string("\1\2\3"); } }; From cfff2349d79b8336eb3f7e92075ded516d79da06 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:13:50 -0700 Subject: [PATCH 34/57] `bool(stream)` => `static_cast(stream)` --- .../LWG2381_num_get_floating_point/test.cpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 5b1cc2109b..d19d332024 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -296,7 +296,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << hexfloat << x).str() == "0x1.0000020000000p+0"); } { @@ -304,7 +304,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << hexfloat << x).str() == "-0x1.0000020000000p+0"); } } else { @@ -313,7 +313,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << hexfloat << x).str() == "0x1.0000000000001p+0"); } { @@ -321,7 +321,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << hexfloat << x).str() == "-0x1.0000000000001p+0"); } } @@ -332,7 +332,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "0.1"); } { @@ -340,7 +340,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-0.1"); } @@ -350,7 +350,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "9"); } { @@ -358,7 +358,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-9"); } @@ -368,7 +368,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "1"); } { @@ -376,7 +376,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-1"); } { @@ -384,7 +384,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "10"); } { @@ -392,7 +392,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-10"); } @@ -402,7 +402,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "1e+38"); } { @@ -410,7 +410,7 @@ void test_gh3375_gh3376() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-1e+38"); } { @@ -422,7 +422,7 @@ void test_gh3375_gh3376() { assert(!stream); assert(x == HUGE_VALF); } else { - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "1e+308"); } } @@ -435,7 +435,7 @@ void test_gh3375_gh3376() { assert(!stream); assert(x == -HUGE_VALF); } else { - assert(bool(stream)); + assert(static_cast(stream)); assert((ostringstream{} << x).str() == "-1e+308"); } } @@ -462,7 +462,7 @@ void test_gh3378() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((move(os) << x).str() == "2.2250738585072019e-308"); } { @@ -483,7 +483,7 @@ void test_gh3378() { Flt x = 0.0; stream >> x; - assert(bool(stream)); + assert(static_cast(stream)); assert((move(os) << x).str() == "-2.2250738585072019e-308"); } } From 40a8d36be4eab4a5bf2009c2ac3bda86b067d5a3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:14:42 -0700 Subject: [PATCH 35/57] Fix comment typo. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index d19d332024..bb7fbbcfc1 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -396,7 +396,7 @@ void test_gh3375_gh3376() { assert((ostringstream{} << x).str() == "-10"); } - // Ensure that huge "999...999.999...999" represantations are correctly parsed. + // Ensure that huge "999...999.999...999" representations are correctly parsed. { istringstream stream(string(38, '9') + "." + string(800, '9')); Flt x = 0.0; From 3cf000dddc7bad1efc1aa5e01813bdc1e42da3df Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:15:56 -0700 Subject: [PATCH 36/57] Make comment vendor-neutral. --- tests/libcxx/expected_results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 053ae0a70a..9e2e98f567 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -670,7 +670,7 @@ 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 as precise as the Intel 80-bit format; also affected by LWG-2381 +# 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 From 700e4c6069d48e288bb8f017b6a20b9458d9acbd Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:16:51 -0700 Subject: [PATCH 37/57] Add LLVM banner. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index bb7fbbcfc1..4bbe6da512 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -1,6 +1,14 @@ // 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 From d2f9aa3dfff15a965a175b2bad7ddca3ba82f310 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:17:57 -0700 Subject: [PATCH 38/57] `0X or 0x` => `0x or 0X` --- stl/inc/xlocnum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 319a2c15e2..a396779d5e 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -300,9 +300,9 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba return 0; } - if (*_First == _Atoms[_Offset_lower_x] || *_First == _Atoms[_Offset_upper_x]) { // 0X or 0x + 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 + ++_First; // discard 0x or 0X for further parsing *_Ptr++ = 'x'; } else { ++_Initial_dec_leading_zero; From 61784c9304692b7f0055376346511c795be3e92b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:19:25 -0700 Subject: [PATCH 39/57] Consistently test `_Significant >= _Max_sig_dig`. --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index a396779d5e..158d012875 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -323,7 +323,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba if (_Grouping.empty()) { for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; _Seendigit = true, (void) ++_First) { - if (_Max_sig_dig <= _Significant) { + if (_Significant >= _Max_sig_dig) { ++_Power_of_rep_base; // just scale by 10 or 16 if (_Idx > 0) { _Sticky = true; From 60a42cfb3859e50f4ccad38ff22f02bff5478a13 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:22:58 -0700 Subject: [PATCH 40/57] Use decimal literals for all offsets. --- stl/inc/xlocnum | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 158d012875..b5d0244e48 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -263,13 +263,13 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba 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 = _Offset_neg_sign + 1; + constexpr size_t _Offset_pos_sign = 23; constexpr size_t _Offset_upper_x = 24; - constexpr size_t _Offset_lower_x = _Offset_upper_x + 1; + constexpr size_t _Offset_lower_x = 25; constexpr size_t _Offset_upper_p = 26; - constexpr size_t _Offset_lower_p = _Offset_upper_p + 1; - constexpr size_t _Offset_upper_e = 0xE; - constexpr size_t _Offset_lower_e = 0xE + 0x10 - 10; + 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()); From 09698d1797c3b465ae8a5954ebf6f7087f7d51a7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:24:45 -0700 Subject: [PATCH 41/57] `_Base` => `_Pten` --- stl/inc/xlocnum | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index b5d0244e48..6d83bb1a7f 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -773,14 +773,14 @@ protected: _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field + const int _Pten = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0f; } else { int _Errno; char* _Ep; - _Val = _STD _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert + _Val = _STD _Stofx_v2(_Ac, &_Ep, _Pten, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { @@ -800,14 +800,14 @@ protected: _Adl_verify_range(_First, _Last); char _Ac[_FLOATING_BUFFER_SIZE]; int _Hexexp = _ENABLE_V2_BEHAVIOR; - const int _Base = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field + const int _Pten = _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516 _State = ios_base::failbit; _Val = 0.0; } else { int _Errno; char* _Ep; - _Val = _STD _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert + _Val = _STD _Stodx_v2(_Ac, &_Ep, _Pten, &_Errno); // convert if (_Ep == _Ac || _Errno != 0) { _State = ios_base::failbit; } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { From 85145992f51b577fbd1b541c845de31809340267 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Apr 2023 19:27:43 -0700 Subject: [PATCH 42/57] Locally scope `size_t _Idx`. --- stl/inc/xlocnum | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 6d83bb1a7f..936ff4cba1 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -311,7 +311,6 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - size_t _Idx; bool _Sticky = false; bool _Seendigit = false; // seen a digit in input bool _Bad = false; @@ -321,7 +320,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba const size_t _Offset_digit_end = _Parse_hex ? _Offset_hex_digit_end : _Offset_dec_digit_end; const string _Grouping = _Punct_fac.grouping(); if (_Grouping.empty()) { - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + 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 @@ -339,7 +338,8 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba size_t _Group_index = 0; for (; _First != _Last; ++_First) { - if ((_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end) { // 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 (_Significant >= _Max_sig_dig) { ++_Power_of_rep_base; // just scale by 10 or 16 @@ -410,7 +410,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba } } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + 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]; @@ -454,7 +454,7 @@ int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_ba *_Ptr++ = '0'; // put one back } - for (; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; + for (size_t _Idx; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; _Seendigit = true, (void) ++_First) { if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit *_Ptr++ = _Src[_Idx]; From 98e6a5e938a8dfe35ba1c6ff1f55a97ad187339d Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 20 Apr 2023 01:18:31 +0800 Subject: [PATCH 43/57] Address review comments And - fix GH-1582 : "multiply by power of 10" logic is imprecise - note DevCom-10293606 --- stl/inc/xlocmon | 2 +- stl/inc/xlocnum | 597 ++++++++++-------- .../LWG2381_num_get_floating_point/test.cpp | 136 ++++ 3 files changed, 470 insertions(+), 265 deletions(-) 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 936ff4cba1..4411f97fb9 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) { // 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,230 +243,10 @@ __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id; #endif // __clang__ #endif // !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) -// TRANSITION, ABI: Sentinel value used by num_get::do_get() to enable correct "V2" behavior -#define _ENABLE_V2_BEHAVIOR 1000000000 - -template -int _Parse_fp_with_ios_base(char* const _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase, int* _Phexexp) { - // get floating-point field from [_First, _Last) into _Ac - const auto& _Punct_fac = _STD use_facet>(_Iosbase.getloc()); - char* _Ptr = _Ac; - - 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()); - _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 == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - *_Ptr++ = '0'; // backstop carries from sticky bit - - bool _Parse_hex = false; - char _Initial_dec_leading_zero = '\0'; - if (_First != _Last && *_First == _Zero_wc) { - ++_First; - if (_First == _Last) { // "0" only - *_Ptr = '\0'; - return 0; - } - - 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 { - ++_Initial_dec_leading_zero; - } - } - - const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1); - - bool _Sticky = false; - bool _Seendigit = false; // seen a digit in input - bool _Bad = false; - int _Significant = 0; // number of significant digits - int _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 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) { - _Sticky = true; - } - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } else { - const _Elem _Kseparator = _Punct_fac.thousands_sep(); - string _Groups(1, _Initial_dec_leading_zero); - size_t _Group_index = 0; - - for (; _First != _Last; ++_First) { - const size_t _Idx = _STD _Find_elem(_Atoms, *_First); - if (_Idx < _Offset_digit_end) { // got a digit, add to group size - _Seendigit = true; - if (_Significant >= _Max_sig_dig) { - ++_Power_of_rep_base; // just scale by 10 or 16 - if (_Idx > 0) { - _Sticky = true; - } - } else if (_Idx != 0 || _Significant != 0) { // save a significant digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - - if (_Groups[_Group_index] != CHAR_MAX) { - ++_Groups[_Group_index]; - } - } else if (_Groups[_Group_index] == '\0' || *_First != _Kseparator) { - break; // not a group separator, done - } else { // add a new group to _Groups string - _Groups.push_back('\0'); - ++_Group_index; - } - } - - if (_Group_index != 0) { - if (_Groups[_Group_index] > '\0') { - ++_Group_index; // add trailing group to group count - } else { - _Bad = true; // trailing separator, fail - } - } - - const char* _Grouping_iter = _Grouping.data(); - const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - char _Current_grouping_count = '\0'; - while (!_Bad && _Group_index > 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; - } - - --_Group_index; - if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) - && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) - || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { - _Bad = true; // bad group size, fail - } - // group size okay, advance to next test - } - } - - if (_Parse_hex && _Seendigit && _Significant == 0) { - *_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++ = _Decimal_point; - ++_First; - } - - if ((_Parse_hex || *_Phexexp != _ENABLE_V2_BEHAVIOR) && _Significant == 0) { // 0000. so far - for (; _First != _Last && *_First == _Zero_wc; _Seendigit = true, (void) ++_First) { - --_Power_of_rep_base; // just count leading fraction zeros - } - - if (_Power_of_rep_base < 0) { // put one back - *_Ptr++ = '0'; - ++_Power_of_rep_base; - } - } - - 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 (_Idx > 0) { - _Sticky = true; // just update _Sticky - } - } - - if (_Sticky) { // increment ls 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 - - if (_Seendigit && _First != _Last && (*_First == _Lower_exp_wc || *_First == _Upper_exp_wc)) { // collect exponent - *_Ptr++ = _Parse_hex ? 'p' : 'e'; - ++_First; - _Seendigit = false; - _Significant = 0; - - if (_First != _Last) { - if (*_First == _Positive_sign) { // gather plus sign - *_Ptr++ = '+'; - ++_First; - } else if (*_First == _Negative_sign) { // gather minus sign - *_Ptr++ = '-'; - ++_First; - } - } - - for (; _First != _Last && *_First == _Zero_wc; ++_First) { // strip leading zeros - _Seendigit = true; - } - - if (_Seendigit) { - *_Ptr++ = '0'; // put one back - } - - for (size_t _Idx; _First != _Last && (_Idx = _STD _Find_elem(_Atoms, *_First)) < _Offset_digit_end; - _Seendigit = true, (void) ++_First) { - if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit - *_Ptr++ = _Src[_Idx]; - ++_Significant; - } - } - } - - if (_Bad || !_Seendigit) { - _Ptr = _Ac; // roll back pointer to indicate failure - } - - *_Ptr = '\0'; - if (_Parse_hex) { - *_Phexexp = _Power_of_rep_base; // power of 16 multiplier - return 0; - } else { - return _Power_of_rep_base; // power of 10 multiplier - } -} +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 @@ -772,19 +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 _Pten = _STD _Parse_fp_with_ios_base<_Elem>(_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 = _STD _Stofx_v2(_Ac, &_Ep, _Pten, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0) { + _Val = _STD _Stofx_v3(_Ac, &_Ep, &_Errno); // convert + if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtual]/3 + || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtual]/4 _State = ios_base::failbit; - } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { - _Val = _CSTD ldexpf(_Val, 4 * _Hexexp); } } @@ -799,19 +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 _Pten = _STD _Parse_fp_with_ios_base<_Elem>(_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 = _STD _Stodx_v2(_Ac, &_Ep, _Pten, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0) { + _Val = _STD _Stodx_v3(_Ac, &_Ep, &_Errno); // convert + if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtual]/3 + || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtual]/4 _State = ios_base::failbit; - } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) { - _Val = _CSTD ldexp(_Val, 4 * _Hexexp); } } @@ -917,8 +687,8 @@ 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_index = 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 = _STD _Find_elem(_Atoms, *_First); @@ -930,20 +700,20 @@ private: } _Seendigit = true; - if (_Groups[_Group_index] != CHAR_MAX) { - ++_Groups[_Group_index]; + if (_Groups[_Groups_arr_idx] != CHAR_MAX) { + ++_Groups[_Groups_arr_idx]; } - } else if (_Groups[_Group_index] == '\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_index; + ++_Groups_arr_idx; } } - if (_Group_index != 0) { - if (_Groups[_Group_index] > '\0') { - ++_Group_index; // 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 } @@ -951,16 +721,16 @@ private: const char* _Grouping_iter = _Grouping.data(); const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); - for (char _Current_grouping_count = '\0'; _Seendigit && _Group_index > 0;) { + 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; } - --_Group_index; + --_Groups_arr_idx; if ((_Current_grouping_count > '\0' && _Current_grouping_count != CHAR_MAX) - && ((_Group_index > 0 && _Groups[_Group_index] != _Current_grouping_count) - || (_Group_index == 0 && _Groups[_Group_index] > _Current_grouping_count))) { + && ((_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 } // group size okay, advance to next test @@ -976,20 +746,319 @@ private: return _Base; } + 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 + const auto& _Punct_fac = _STD use_facet>(_Loc); + char* _Ptr = _Ac; + + 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>(_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 == _Positive_sign) { // gather plus sign + *_Ptr++ = '+'; + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + *_Ptr++ = '-'; + ++_First; + } + } + + *_Ptr++ = '0'; // backstop carries from sticky bit + + bool _Parse_hex = false; + char _Initial_dec_leading_zero = '\0'; + if (_First != _Last && *_First == _Zero_wc) { + ++_First; + if (_First == _Last) { // "0" only + *_Ptr = '\0'; + return {10, false}; + } + + 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 { + ++_Initial_dec_leading_zero; + } + } + + bool _Has_unaccumulated_digits = false; + bool _Seendigit = false; // seen a digit in input + 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 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 { + 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) { + const size_t _Idx = _STD _Find_elem(_Atoms, *_First); + if (_Idx < _Offset_digit_end) { // got a digit, add to group size + _Seendigit = 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[_Groups_arr_idx] != CHAR_MAX) { + ++_Groups[_Groups_arr_idx]; + } + } 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'); + ++_Groups_arr_idx; + } + } + + if (_Groups_arr_idx != 0) { + if (_Groups[_Groups_arr_idx] > '\0') { + ++_Groups_arr_idx; // add trailing group to group count + } else { + _Bad_grouping = true; // trailing separator, fail + } + } + + const char* _Grouping_iter = _Grouping.data(); + const char* const _Grouping_end = _Grouping.data() + _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; + } + + --_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++ = _Decimal_point; + ++_First; + } + + 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 (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 (_Idx > 0) { + _Has_unaccumulated_digits = true; // just update _Has_unaccumulated_digits + } + } + + 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 == _Lower_exp_wc || *_First == _Upper_exp_wc)) { // collect exponent + ++_First; + _Seendigit = false; + _Significant = 0; + + if (_First != _Last) { + if (*_First == _Positive_sign) { // gather plus sign + ++_First; + } else if (*_First == _Negative_sign) { // gather minus sign + _Exponent_part_negative = true; + ++_First; + } + } + + for (; _First != _Last && *_First == _Zero_wc; ++_First) { // strip leading zeros + _Seendigit = true; + } + + if (_Seendigit) { + *_Ptr++ = '0'; // put one back + } + + 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 (_Exponent_part_negative) { + _Exponent_part = -_Exponent_part; + } + } + + if (!_Seendigit) { + return {0, false}; + } + + 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 + + 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; + + // 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; + } + 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 { + _Exponent_part += _Parse_hex ? _Exp_rep_adjustment * 4 : _Exp_rep_adjustment; + _Power_of_rep_adjusted -= _Exp_rep_adjustment; + } + } + } + + if (_Exponent_part != 0) { + *_Ptr++ = _Parse_hex ? 'p' : 'e'; + if (_Exponent_part < 0) { + *_Ptr++ = '-'; + } + + 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); + } + + *_Ptr = '\0'; + return {_Parse_hex ? 16 : 10, _Bad_grouping}; + } + +// TRANSITION, ABI: Sentinel value used by num_get::do_get() +// to enable correct "V2" behavior in _Getffld() and _Getffldx() +#define _ENABLE_V2_BEHAVIOR 1000000000 + 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 - return _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, _Phexexp); + 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'; + } + if (_Parse_result._Base == 16) { + *_Phexexp = 0; // power of 16 multiplier, unnecessary now + } + return 0; // power of 10 multiplier, unnecessary now } 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 - return _STD _Parse_fp_with_ios_base<_Elem>(_Ac, _First, _Last, _Iosbase, _Phexexp); + 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'; + } + 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 +}; #ifdef __clang__ #pragma clang diagnostic push diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 4bbe6da512..5344105f65 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -24,6 +24,15 @@ #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 { @@ -292,6 +301,83 @@ void test_int_grouping() { } } +// Also test GH-1582 : "multiply by power of 10" logic is imprecise +template +void test_gh1582() { + { + istringstream iss{"1" + string(2000, '0') + "e-2000"}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"0." + string(2000, '0') + "1e+2001"}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"0." + string(2000, '0') + "1e2001"}; + Flt x{}; + assert(iss >> x); + assert(x == 1.0); + } + { + istringstream iss{"2" + string(2000, '0') + "e-2000"}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0." + string(2000, '0') + "2e+2001"}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0." + string(2000, '0') + "2e2001"}; + Flt x{}; + assert(iss >> x); + assert(x == 2.0); + } + { + istringstream iss{"0x1" + string(2000, '0') + "p-8000"}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0x0." + string(2000, '0') + "1p+8004"}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0x0." + string(2000, '0') + "1p8004"}; + Flt x{}; + assert(iss >> x); + assert(x == 0x1.0p0); + } + { + istringstream iss{"0xA" + string(2000, '0') + "p-8000"}; + Flt x{}; + assert(iss >> x); + assert(x == 0xA.0p0); + } + { + istringstream iss{"0x0." + string(2000, '0') + "Ap+8004"}; + Flt x{}; + assert(iss >> x); + assert(x == 0xA.0p0); + } + { + istringstream iss{"0x0." + string(2000, '0') + "Ap8004"}; + 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 // And GH-3376 : Incorrect result when parsing 9.999999... @@ -496,6 +582,46 @@ void test_gh3378() { } } +#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" + std::string(test_case.input) : std::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" + std::string(test_case.input) : std::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(); @@ -507,10 +633,20 @@ int main() { test_int_grouping(); test_int_grouping(); + test_gh1582(); + test_gh1582(); + test_gh1582(); + test_gh3375_gh3376(); test_gh3375_gh3376(); test_gh3375_gh3376(); test_gh3378(); test_gh3378(); + +#if _HAS_CXX17 + test_float_from_char_cases(); + test_double_from_char_cases(); + test_double_from_char_cases(); +#endif //_HAS_CXX17 } From 721ed3f8dd702066c7dd17e8b2099a427ece3a1f Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 20 Apr 2023 01:27:19 +0800 Subject: [PATCH 44/57] Missing Clang-format --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 5344105f65..ee8e40039f 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -583,8 +583,7 @@ void test_gh3378() { } #if _HAS_CXX17 -void test_float_from_char_cases() -{ +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" + std::string(test_case.input) : std::string(test_case.input); From 24c65d56705ecd30660be509ebbbab5c6636a3f0 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 20 Apr 2023 01:50:35 +0800 Subject: [PATCH 45/57] No narrowing! --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 4411f97fb9..85e023dc3f 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1022,7 +1022,7 @@ private: } *_Ptr = '\0'; - return {_Parse_hex ? 16 : 10, _Bad_grouping}; + return {static_cast(_Parse_hex ? 16 : 10), _Bad_grouping}; } // TRANSITION, ABI: Sentinel value used by num_get::do_get() From 0dd8f17fc2d9cecfa2bfd6380e5bf9bc649d0eb1 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 22 Apr 2023 01:05:26 +0800 Subject: [PATCH 46/57] Strengthen tests for GH-1582 and exception specification --- stl/inc/xlocnum | 2 +- .../LWG2381_num_get_floating_point/test.cpp | 145 +++++++++--------- 2 files changed, 75 insertions(+), 72 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 85e023dc3f..924eaa494c 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -48,7 +48,7 @@ inline double _Stodx_v3(const char* _Str, char** _Endptr, int* _Perr) noexcept { return _Val; } -inline float _Stofx_v3(const char* _Str, char** _Endptr, 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; diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index ee8e40039f..0220b40450 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -304,77 +304,80 @@ void test_int_grouping() { // Also test GH-1582 : "multiply by power of 10" logic is imprecise template void test_gh1582() { - { - istringstream iss{"1" + string(2000, '0') + "e-2000"}; - Flt x{}; - assert(iss >> x); - assert(x == 1.0); - } - { - istringstream iss{"0." + string(2000, '0') + "1e+2001"}; - Flt x{}; - assert(iss >> x); - assert(x == 1.0); - } - { - istringstream iss{"0." + string(2000, '0') + "1e2001"}; - Flt x{}; - assert(iss >> x); - assert(x == 1.0); - } - { - istringstream iss{"2" + string(2000, '0') + "e-2000"}; - Flt x{}; - assert(iss >> x); - assert(x == 2.0); - } - { - istringstream iss{"0." + string(2000, '0') + "2e+2001"}; - Flt x{}; - assert(iss >> x); - assert(x == 2.0); - } - { - istringstream iss{"0." + string(2000, '0') + "2e2001"}; - Flt x{}; - assert(iss >> x); - assert(x == 2.0); - } - { - istringstream iss{"0x1" + string(2000, '0') + "p-8000"}; - Flt x{}; - assert(iss >> x); - assert(x == 0x1.0p0); - } - { - istringstream iss{"0x0." + string(2000, '0') + "1p+8004"}; - Flt x{}; - assert(iss >> x); - assert(x == 0x1.0p0); - } - { - istringstream iss{"0x0." + string(2000, '0') + "1p8004"}; - Flt x{}; - assert(iss >> x); - assert(x == 0x1.0p0); - } - { - istringstream iss{"0xA" + string(2000, '0') + "p-8000"}; - Flt x{}; - assert(iss >> x); - assert(x == 0xA.0p0); - } - { - istringstream iss{"0x0." + string(2000, '0') + "Ap+8004"}; - Flt x{}; - assert(iss >> x); - assert(x == 0xA.0p0); - } - { - istringstream iss{"0x0." + string(2000, '0') + "Ap8004"}; - Flt x{}; - assert(iss >> x); - assert(x == 0xA.0p0); + 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); + } } } From 5a2bfb28e5724124aa122955094511fac43bd8b8 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:18:36 -0700 Subject: [PATCH 47/57] Fix stable name. --- stl/inc/xlocnum | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 924eaa494c..134793b387 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -553,8 +553,8 @@ protected: int _Errno; char* _Ep; _Val = _STD _Stofx_v3(_Ac, &_Ep, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtual]/3 - || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtual]/4 + 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; } } @@ -579,8 +579,8 @@ protected: int _Errno; char* _Ep; _Val = _STD _Stodx_v3(_Ac, &_Ep, &_Errno); // convert - if (_Ep == _Ac || _Errno != 0 // N4944 [facet.num.get.virtual]/3 - || _Parse_result._Bad_grouping) { // N4944 [facet.num.get.virtual]/4 + 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; } } From a278ae3aae500b3b44134c1b9a80cfac278ee756 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:30:44 -0700 Subject: [PATCH 48/57] Use `const auto& _Ctype_fac` for consistency. --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 134793b387..372c85ddbe 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -764,7 +764,7 @@ private: 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>(_Loc); + 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]; From a4c24d5c24443c24d7571d5f4ca9fe34488fe712 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:32:46 -0700 Subject: [PATCH 49/57] Delay `const auto& _Punct_fac` until we need it. --- stl/inc/xlocnum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 372c85ddbe..dbbe72c88f 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -749,8 +749,7 @@ private: 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 - const auto& _Punct_fac = _STD use_facet>(_Loc); - char* _Ptr = _Ac; + char* _Ptr = _Ac; constexpr size_t _Offset_dec_digit_end = 10; constexpr size_t _Offset_hex_digit_end = 22; @@ -808,6 +807,7 @@ private: 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; From 9dd77bd2f51905d99c92a8741e6143339c60eff5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:42:01 -0700 Subject: [PATCH 50/57] Use `_Grouping_iter`. --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index dbbe72c88f..85fcc6fa94 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -861,7 +861,7 @@ private: } const char* _Grouping_iter = _Grouping.data(); - const char* const _Grouping_end = _Grouping.data() + _Grouping.size(); + 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 From 428e36d51630f2d3a03d955da2fd7a9e7140d999 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:51:28 -0700 Subject: [PATCH 51/57] Add newlines between non-chained if-statements. --- stl/inc/xlocnum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 85fcc6fa94..4f2cb3471f 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1037,6 +1037,7 @@ private: if (_Parse_result._Base == 0 || _Parse_result._Bad_grouping) { // TRANSITION, ABI, old behavior *_Ac = '\0'; } + if (_Parse_result._Base == 16) { *_Phexexp = 0; // power of 16 multiplier, unnecessary now } @@ -1051,6 +1052,7 @@ private: if (_Parse_result._Base == 0 || _Parse_result._Bad_grouping) { // TRANSITION, ABI, old behavior *_Ac = '\0'; } + if (_Parse_result._Base == 16) { *_Phexexp = 0; // power of 16 multiplier, unnecessary now } From ee40b665759dcebff6c62aca464f7a3000a7f092 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 00:58:22 -0700 Subject: [PATCH 52/57] Use a string UDL for consistency. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 0220b40450..57900a876c 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -62,7 +62,7 @@ class my_numpunct : public numpunct { return '_'; } string do_grouping() const override { - return string("\1\2\3"); + return "\1\2\3"s; } }; From 69d4d59cb26e1436d2e96181a3599dbc051346de Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 01:05:07 -0700 Subject: [PATCH 53/57] Add spaces to comments. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 57900a876c..03cfe8280f 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -622,7 +622,7 @@ void test_double_from_char_cases() { } } } -#endif //_HAS_CXX17 +#endif // _HAS_CXX17 int main() { test(); @@ -650,5 +650,5 @@ int main() { test_float_from_char_cases(); test_double_from_char_cases(); test_double_from_char_cases(); -#endif //_HAS_CXX17 +#endif // _HAS_CXX17 } From 5d21b8a37e29e93443691f173e40bb10e9ebee59 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 26 Apr 2023 01:07:10 -0700 Subject: [PATCH 54/57] Drop `std` qualification. --- tests/std/tests/LWG2381_num_get_floating_point/test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 03cfe8280f..6f2315e8ca 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -588,8 +588,7 @@ void test_gh3378() { #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" + std::string(test_case.input) : std::string(test_case.input); + auto repstr = test_case.fmt == chars_format::hex ? "0x" + string(test_case.input) : string(test_case.input); istringstream is(repstr); float x = 0.0f; @@ -607,8 +606,7 @@ void test_float_from_char_cases() { 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" + std::string(test_case.input) : std::string(test_case.input); + auto repstr = test_case.fmt == chars_format::hex ? "0x" + string(test_case.input) : string(test_case.input); istringstream is(repstr); Flt x = 0.0; From 7781f2e1adb1bf49489c9c8f22fe467733550dfb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 27 Apr 2023 12:34:08 -0700 Subject: [PATCH 55/57] Avoid changing the dllexport surface. --- stl/inc/xlocnum | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 4f2cb3471f..581e8686cc 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -746,6 +746,7 @@ private: return _Base; } + 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 @@ -1032,6 +1033,9 @@ private: 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 @@ -1047,6 +1051,9 @@ private: 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 From 6ce6f9ec3cb607e4ea3fadca02ca8bc41c44653e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 3 May 2023 17:46:28 +0800 Subject: [PATCH 56/57] Fix handling of `"0\n"` and add test coverage --- stl/inc/xlocnum | 3 +- .../LWG2381_num_get_floating_point/test.cpp | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 581e8686cc..752f9a0d98 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -784,6 +784,7 @@ private: *_Ptr++ = '0'; // backstop carries from sticky bit 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; @@ -797,12 +798,12 @@ private: ++_First; // discard 0x or 0X for further parsing *_Ptr++ = 'x'; } else { + _Seendigit = true; ++_Initial_dec_leading_zero; } } bool _Has_unaccumulated_digits = false; - bool _Seendigit = false; // seen a digit in input bool _Bad_grouping = false; int _Significant = 0; // number of significant digits ptrdiff_t _Power_of_rep_base = 0; // power of 10 or 16 diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index 6f2315e8ca..cfd70efcec 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -203,6 +203,56 @@ void test() { 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 instr.imbue(locale(locale(), new mid_zero_numpunct)); { From 183192d03eecb661080252fd7a27d70943b58c4b Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 3 May 2023 17:56:38 +0800 Subject: [PATCH 57/57] Split functions in the test file --- .../LWG2381_num_get_floating_point/test.cpp | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp index cfd70efcec..866cee0247 100644 --- a/tests/std/tests/LWG2381_num_get_floating_point/test.cpp +++ b/tests/std/tests/LWG2381_num_get_floating_point/test.cpp @@ -252,9 +252,15 @@ void test() { assert(v == 0); assert(str_instr.good()); } +} - // Also test non-ending unlimited grouping +// 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"; @@ -298,8 +304,8 @@ void test() { } // Also test non-ending unlimited grouping for integers -template -void test_int_grouping() { +template , int> = 0> +void test_nonending_unlimited_grouping() { const my_facet f(1); ios instr(nullptr); instr.imbue(locale(locale(), new mid_zero_numpunct)); @@ -353,7 +359,7 @@ void test_int_grouping() { // Also test GH-1582 : "multiply by power of 10" logic is imprecise template -void test_gh1582() { +void test_gh_1582() { constexpr size_t digit_counts[]{200, 400, 800, 1600, 3200, 6400, 12800}; for (const size_t n : digit_counts) { { @@ -433,9 +439,8 @@ void test_gh1582() { // Also test GH-3375 : Incorrect rounding when parsing long hexadecimal floating point numbers just above // midpoints -// And GH-3376 : Incorrect result when parsing 9.999999... template -void test_gh3375_gh3376() { +void test_gh_3375() { // Ensure long hexadecimal FP representations just above midpoints are correctly parsed. if (is_same_v) { { @@ -472,6 +477,11 @@ void test_gh3375_gh3376() { 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. { @@ -590,7 +600,7 @@ void test_gh3375_gh3376() { // Also test GH-3378: : Incorrect rounding when parsing long floating point numbers just below midpoints template -void test_gh3378() { +void test_gh_3378() { { // just below 2^-1022 + 2^-1074 + 2^-1075 istringstream stream( @@ -677,22 +687,30 @@ int main() { test(); test(); - test_int_grouping(); - test_int_grouping(); - test_int_grouping(); - test_int_grouping(); - test_int_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_nonending_unlimited_grouping(); + + test_gh_1582(); + test_gh_1582(); + test_gh_1582(); - test_gh1582(); - test_gh1582(); - test_gh1582(); + test_gh_3375(); + test_gh_3375(); + test_gh_3375(); - test_gh3375_gh3376(); - test_gh3375_gh3376(); - test_gh3375_gh3376(); + test_gh_3376(); + test_gh_3376(); + test_gh_3376(); - test_gh3378(); - test_gh3378(); + test_gh_3378(); + test_gh_3378(); #if _HAS_CXX17 test_float_from_char_cases();